我正在为CLR脚本编写.NET On-the-Fly编译器,并希望执行方法使得泛型可接受:
object Execute()
{
return type.InvokeMember(..);
}
T Execute<T>()
{
return Execute() as T; /* doesn't work:
The type parameter 'T' cannot be used with the 'as' operator because
it does not have a class type constraint nor a 'class' constraint */
// also neither typeof(T) not T.GetType(), so on are possible
return (T) Execute(); // ok
}
但我认为运算符as
非常有用:如果结果类型不是T
,则方法将返回null
,而不是异常!有可能吗?
答案 0 :(得分:56)
您需要添加
where T : class
到您的方法声明,例如
T Execute<T>() where T : class
{
顺便提一下,作为一个建议,通用包装器并没有真正增加太多价值。来电者可以写:
MyClass c = whatever.Execute() as MyClass;
或者如果他们想要失败:
MyClass c = (MyClass)whatever.Execute();
通用包装器方法如下所示:
MyClass c = whatever.Execute<MyClass>();
所有三个版本必须指定完全相同的三个实体,只是以不同的顺序,所以没有更简单或更方便,但通用版本隐藏了正在发生的事情,而“原始”版本各自使它清楚是否会抛出或null
。
(如果您的示例已从实际代码中简化,这可能与您无关。)
答案 1 :(得分:11)
您不能将as
运算符用于没有限制的泛型类型。由于as
运算符使用null来表示它不是类型,因此不能在值类型上使用它。如果您想使用obj as T
,T
将 作为参考类型。
T Execute<T>() where T : class
{
return Execute() as T;
}
答案 2 :(得分:3)
这小段代码可以安全替换 as 关键字:
return Execute() is T value ? value : default(T)
它使用C#7中引入的模式匹配功能。 如果您不想将通用参数限制为引用类型,请使用它
答案 3 :(得分:1)
您似乎只是添加了一个包装方法,用于转换为用户想要的类型,因此只会增加执行的开销。对于用户,写
int result = Execute<int>();
与
没什么不同int result = (int)Execute();
您可以使用 out 修饰符将结果写入调用者范围内的变量,并返回一个布尔标志来判断它是否成功:
bool Execute<T>(out T result) where T : class
{
result = Execute() as T;
return result != null;
}
答案 4 :(得分:1)
Execute()是否有可能返回值类型?如果是这样,那么你需要Earwicker的类类型方法,以及值类型的另一种通用方法。可能看起来像这样:
Nullable<T> ExecuteForValueType<T> where T : struct
该方法内部的逻辑会说
object rawResult = Execute();
然后,您必须获取rawResult的类型并查看是否可以将其分配给T:
Nullable<T> finalReturnValue = null;
Type theType = rawResult.GetType();
Type tType = typeof(T);
if(tType.IsAssignableFrom(theType))
{
finalReturnValue = tType;
}
return finalReturnValue;
最后,让你的原始执行消息找出T所具有的(类或结构类型),并调用相应的实现。
注意:这是来自粗糙的记忆。大约一年前我这样做了,可能不记得每一个细节。不过,我希望能指出你在大方向上的帮助。