I have a generic method with this signature:
private void MyGenericMethod<开发者_如何学C;T>(T arg) where T : class
{}
If I pass, say, an integer to this method, I get an ArgumentException that the passed value does not match the restriction. This is great, but how can I predetermine that what I am passing will match the "class" constraint so that the exception is not thrown?
The compiler will already do that for you - you should actually see:
The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'blah.MyGenericMethod(T)'
at compile-time.
The tricky scenarios are:
- generics upon generics upon generics - all those type-constraints stack, so you end up with
where T : class
a lot. Sometimes it is better to use runtime validation againstT
- reflection (
MakeGenericMethod
, etc) - again, just check at runtime
Also, note that where T : class
doesn't actually mean T
is a class - it means it is a reference-type, which can include interfaces and delegates. Likewise, where T : struct
doesn't actually mean T
is a struct
- it means it is a struct that is not Nullable<>
.
You can not pass int
because it is a value type and you have constrained your Method to accept only reference type.
if you want to support any of those you need to remove the generics constrain like following
private void MyGenericMethod<T>(T arg)
{
if(arg.GetType().IsValueType)
{
//T is value type
}
}
You actually get compilation errors if the argument being passed is not a reference type. If you box a value type, however, you can get around that, but then it's a valid reference type.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication29
{
class Program
{
static void Main(string[] args)
{
int i = 0;
object o = (object)i;
MyMethod(o);
MyMethod(i); // Doesn't compile.
}
static void MyMethod<T>(T arg) where T : class
{
}
}
}
If you are getting this error because you do something at runtime, such as use reflection to call the method, or you box value types, then you will simply need to check the parameter before you use it:
static void MyMethod<T>(T arg) where T : class
{
if (arg is ValueType)
throw new ArgumentException();
}
Note that this will catch all value types, whether they are boxed or not. Also note that using is
also matches true for the type in it's hierarchy (base / derived classes), whereas checking GetType
against typeof
takes only the type at that level:
int i = 0;
bool b = i is object;
b = i.GetType() == typeof(object);
Obviously in your case you won't want to throw an ArgumentException
and perhaps just do nothing.
精彩评论