問題是這樣的: 我想要一個具有泛型類型的參數的泛型函數。 將泛型類型限制爲引用類型,並且沒有課程問題。 但我想要一個完全不受限制的泛型類型!沒有新的()或類/結構限制!如何擁有一個沒有任何類型限制的通用輸出參數
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。 任務完成!
只是爲了防止有人建議我用一個參考,而不是出來:沒有,因爲那時我已經在*之前初始化*我將它傳遞給Test()。完全打敗了整個想法。 – AnorZaken
是什麼問題? – BrokenGlass
TL;通過半途後的DR。看起來你是通過解決自己的問題來完成的。儘管提問併發布答案也很好,但請將答案作爲答案發布,而不是在問題中。即使這不是一個好的答案,也可以工作,並且您正在尋找其他答案。或者它可以工作,如果你猛扣扣它 - 我們不挑剔:) –