2011-12-11 33 views
2

問題是這樣的: 我想要一個具有泛型類型的參數的泛型函數。 將泛型類型限制爲引用類型,並且沒有課程問題。 但我想要一個完全不受限制的泛型類型!沒有新的()或類/結構限制!如何擁有一個沒有任何類型限制的通用輸出參數

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

上面的代碼說明了什麼我想做。我想設置輸出參數,但只能在與所描述的條件類似的條件下。在Test(...)的假情況下,我對out t的價值完全不感興趣。但以上是當然不工作的代碼。 上面的問題是out參數必須被初始化。但是可能初始化有時很昂貴(取決於類型T),我不想初始化一個虛擬類實例,只是爲了讓編譯器停止抱怨。於是問題就變成了:你如何初始化一個未知類型(並且確保它是初始化爲空的,如果它是一個類的)?

那麼從理論上講,你應該能夠寫類似

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#中的所有東西都有特殊的關係。

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

編譯 ATLEAST。但它仍然是垃圾。運行時錯誤嘉豪。 值類型的問題是,對象類型記得它是什麼類型真的是是什麼,當試圖投到別的東西......奇怪的事情發生(無窮?真的??) 這該死的好該應該可行!所以讓我們更多創意!

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是一個引用類型,它會被初始化爲null。 仍然是值類型的問題。有些不情願的創造力將注意力轉向反思。經過一些隨機的周圍挖上反射的東西,找東西值得嘗試(!沒有你不能得到構造函數的值類型,則返回null)我碰到一個小紙條偶然在MSDN:

「要創建一個沒有實例 構造函數的值類型的實例,請使用CreateInstance方法。」

輸入CreateInstance<T>() - http://msdn.microsoft.com/en-us/library/0hcyx2kd.aspx

「的通用的CreateInstance方法用於由編譯器來實現 的按類型參數指定類型的實例化」。

現在我們正在某個地方!當然它說

「一般來說,對於在應用 代碼的CreateInstance沒有用,因爲這個類型必須在編譯時是已知的。如果類型是 在編譯時已知,正常的實例語法可以(在C#中使用新的 運算符,在Visual Basic中使用新的,在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)中測試和運行的代碼。

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

而光榮「輸出」(來自當地人面板@斷點):

 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 ,類實例爲null。 任務完成!

+0

只是爲了防止有人建議我用一個參考,而不是出來:沒有,因爲那時我已經在*之前初始化*我將它傳遞給Test()。完全打敗了整個想法。 – AnorZaken

+1

是什麼問題? – BrokenGlass

+0

TL;通過半途後的DR。看起來你是通過解決自己的問題來完成的。儘管提問併發布答案也很好,但請將答案作爲答案發布,而不是在問題中。即使這不是一個好的答案,也可以工作,並且您正在尋找其他答案。或者它可以工作,如果你猛扣扣它 - 我們不挑剔:) –

回答

8

你的解決辦法似乎是不必要的 - 你只需要default(T)它同時適用於參考和值類型:

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

確實,哈哈,我知道它應該是可行的......有時你會忘記最簡單的事情^^ – AnorZaken

相關問題