如何拥有没有任何类型限制的泛型输出参数

本文关键字:泛型 输出 参数 何拥 有没有 任何 类型 | 更新日期: 2023-09-27 18:17:33

问题是:我想要一个泛型函数,它有泛型类型的外参数。将泛型类型限制为ref类型当然没有问题。但是我想要一个完全不受限制的泛型!没有new()或类/结构限制!

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是值类型时,我们应该能够创建它,但编译器不允许。第二个问题类似:"它可能是一个值类型!"-不,我只是确定不是。它应该工作,但它没有。很烦人。

Ok。所以我们开始有创意…毕竟,有一个很好的类叫做Object,它和c#中的所有东西都有特殊的关系。

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;
  }
}

这个至少编译。但它仍然是垃圾。运行时错误的。值类型的问题是,对象类型记得它真正的类型,当试图转换为其他类型时……奇怪的事情发生了(无限?真的吗? ?)这该死的应该是可行的!所以让我们更有创意吧!

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类型,它被初始化为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;
  }
}

,砰!就是这样!它完全工作所以好!以下是在VS2010SP1和MonoDevelop(使用Unity3.4)中测试和运行的一些代码

使用系统;

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
    }
}

和辉煌的"输出"(来自local_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,类实例为空。任务完成!

如何拥有没有任何类型限制的泛型输出参数

您的解决方案似乎没有必要-您只需要default(T),它适用于引用和值类型:

public static bool Test<T>(ref A a, out T t)
{
  t = default(T);
  return true;
}