问题在于: 我想要一个泛型函数,它具有泛型类型的out参数。 将泛型类型限制为ref-type,并且没有问题。 但我想要一个完全不受限制的通用类型!没有new()或类/ struct-restrictions!
public class A
{ }
public class B<T> : A
{
public T t;
public B(T i)
{
this.t = t;
}
}
public static class S
{
public static bool Test<T>(ref A a, out T t)
{
C<T> c = a as C<T>;
if(c != null)
{
t = c.t;
return true;
}
else
return false;
}
}
class Run
{
static void Main(string[] args)
{
C<SomeType> c = new C<SomeType>(new SomeType(...));
SomeType thing;
S.Test<SomeType>(c,thing);
}
}
上面的代码说明了我想要做什么。我想设置out参数,但仅在与所描述的条件类似的条件下。在Test(...)
的假例中,我对out t
的价值完全不感兴趣。但上述情况并非工作代码。
上述问题是必须初始化out
参数。但也许初始化有时很昂贵(取决于T
的类型)并且我不想初始化一个虚拟类实例只是为了让编译器停止抱怨。那么问题就变成了:如何初始化一个未知类型(如果它是一个类,确保它被初始化为null)?
public static bool Test<T>(ref A a, out T t)
{
//...
else
{
if(typeof(T).IsValueType)
{
t = (T)0; //Can't cast from int to T error (Yeah, this is a little tricky...)
//OR:
t = new T(); //No-no, doesn't have the new()-restriction on T (But we know it's a value type... life sucks)
}
else
t = null; //Error: Can't set to null! T could be valueType! (No it CAN'T... again life sucks)
return false;
}
}
但唉,这不是那么简单。第一个问题是当T是值类型时,我们应该能够创建它,但编译器不会让我们。第二个问题是类似的:“它可能是一种价值型!” - 不,我只是确定不是。它应该工作,但事实并非如此。非常烦人。
确定。所以我们开始发挥创造力......毕竟,有一个叫做Object的漂亮的类,它与C#'ish的所有东西有着特殊的关系。
public static bool Test<T>(ref A a, out T t)
{
//...
else
{
if(typeof(T).IsValueType)
{
t = (T)(object)0; //Works ONLY if T is valuetype: int, otherwise you get a "must be less than infinity"-error.
}
else
{
t = (T)(object)null; //Null ref exception in the cast...
}
return false;
}
}
此至少编译。但它仍然是垃圾。运行时错误很多。 value-type的问题在于,object-type会记住真正的类型,以及何时尝试转换为其他东西......奇怪的事情发生了(无限?真的吗??) 那该死的井应该是可行的!所以,让我们更多创意!
public static bool Test<T>(ref A a, out T t)
{
//...
else
{
if(typeof(T).IsValueType)
{
//... still rubbish here
}
else
{
object o = null;
t = (T)o; //Tricked you stupid compiler!
}
return false;
}
}
没错!它看起来是一个愚蠢的微不足道的变化...但是这个编译 - 对于非值类型,运行和给出了我们想要的结果!如果T是ref-type,它将被初始化为null。 仍然是价值类型的问题。有点不情愿的创造力将注意力转向反思。经过一些随机挖掘反射的东西,寻找值得尝试的东西(并且没有!你不能得到值类型的构造函数,它返回null)我偶然发现了一个关于msdn的小注释:
“创建没有实例的值类型的实例 构造函数,使用CreateInstance方法。“
输入CreateInstance<T>()
- http://msdn.microsoft.com/en-us/library/0hcyx2kd.aspx。
“编译器使用CreateInstance泛型方法来实现 由类型参数指定的类型的实例化。“
现在我们到了某个地方!当然可以说
“通常,应用程序中的CreateInstance没有用处 代码,因为必须在编译时知道类型。如果类型是 在编译时已知,可以使用正常的实例化语法(新的 C#中的operator,Visual Basic中的New,C ++中的gcnew。“
但是,嘿 - 我们并不是一般性的东西,我们处于创造性模式,编译器对我们来说是脾气暴躁。完全有理由尝试一下。
public static bool Test<T>(ref A a, out T t)
{
//...
else
{
if(typeof(T).IsValueType)
{
t = Activator.CreateInstance<T>(); //Probably not your everyday code...
}
else
{
object o = null;
t = (T)o;
}
return false;
}
}
AND BAM!就是这样!它完全有效 soo 好! 下面是在VS2010SP1和MonoDevelop(使用Unity3.4)中测试和运行的一些代码
使用System;
namespace GenericUnrestrictedOutParam
{
class Program
{
class TestClass
{
public int i;
}
struct TestStruct
{
int i;
TestClass thing;
};
public static void NullWorkaround<T>(out T anything)
{
if (typeof(T).IsValueType)
{
anything = Activator.CreateInstance<T>();
}
else
{
object o = null;
anything = (T)o;
}
}
static void Main(string[] args)
{
int i;
float f;
string s;
TestStruct ts;
TestClass c;
NullWorkaround<int>(out i);
NullWorkaround<float>(out f);
NullWorkaround<string>(out s);
NullWorkaround<TestStruct>(out ts);
NullWorkaround<TestClass>(out c);
} //Breakpoint here for value-checking
}
}
光荣的“输出”(来自locals-panel @ breakpoint):
args {string[0]} string[]
i 0 int
f 0.0 float
s null string
- ts {GenericUnrestrictedOutParam.Program.TestStruct} GenericUnrestrictedOutParam.Program.TestStruct
i 0 int
thing null GenericUnrestrictedOutParam.Program.TestClass
c null GenericUnrestrictedOutParam.Program.TestClass
即使是带有值和类型类型的结构也能得到很好的处理:值类型为0,类实例为空。 完成任务!
答案 0 :(得分:8)
您的解决方法似乎没必要 - 您只需要default(T)
,它适用于参考和值类型:
public static bool Test<T>(ref A a, out T t)
{
t = default(T);
return true;
}