运算符as和generics类
我正在为CLR脚本编写.NET On-the-Fly编译器,并希望执行方法使得generics可接受:
object Execute() { return type.InvokeMember(..); } T Execute() { 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 }
但我觉得运算符非常有用:如果结果类型不是T
方法将返回null
,而不是exception! 有可能吗?
你需要添加
where T : class
你的方法声明,例如
T Execute() where T : class {
顺便说一下,作为一个建议,通用包装器并没有真正增加太多价值。 来电者可以写:
MyClass c = whatever.Execute() as MyClass;
或者如果他们想要失败:
MyClass c = (MyClass)whatever.Execute();
通用包装器方法如下所示:
MyClass c = whatever.Execute();
所有三个版本必须指定完全相同的三个实体,只是在不同的顺序,所以没有更简单或更方便,但通用版本隐藏了正在发生的事情,而“原始”版本每个都清楚地说明是否会是一个抛出或null
。
(如果您的示例是从实际代码中简化的话,这可能与您无关)。
您不能将as
运算符与没有限制的generics类型一起使用。 由于as
运算符使用null来表示它不是类型,因此不能在值类型上使用它。 如果要将obj as T
,则T
必须是引用类型。
T Execute() where T : class { return Execute() as T; }
这段小代码是as -keyword的exception安全替代:
return Execute() is T value ? value : default(T)
它使用C#7引入的模式匹配function。如果您不想将generics参数限制为引用类型,请使用它
看起来您只是添加了一个包装器方法,用于转换为用户想要的类型,因此只会增加执行的开销。 对于用户来说,写作
int result = Execute();
并没有太大的不同
int result = (int)Execute();
您可以使用out修饰符将结果写入调用者范围内的变量,并返回一个布尔标志来判断它是否成功:
bool Execute(out T result) where T : class { result = Execute() as T; return result != null; }
Execute()是否有可能返回值类型? 如果是这样,那么你需要Earwicker的类类型方法,以及值类型的另一种通用方法。 可能看起来像这样:
Nullable ExecuteForValueType where T : struct
该方法内部的逻辑会说
object rawResult = Execute();
然后,你必须得到rawResult的类型,看看它是否可以分配给T:
Nullable finalReturnValue = null; Type theType = rawResult.GetType(); Type tType = typeof(T); if(tType.IsAssignableFrom(theType)) { finalReturnValue = tType; } return finalReturnValue;
最后,让您的原始Execute消息确定哪个T具有(类或结构类型),并调用适当的实现。
注意:这是来自粗略的记忆。 大约一年前我这样做了,可能不记得每一个细节。 不过,我希望指出你在总体方向上有所帮助。