运算符as和泛型类

时间:2009-03-28 20:27:06

标签: c# .net generics operators on-the-fly

我正在为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,而不是异常!有可能吗?

5 个答案:

答案 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 TT 作为参考类型。

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所具有的(类或结构类型),并调用相应的实现。

注意:这是来自粗糙的记忆。大约一年前我这样做了,可能不记得每一个细节。不过,我希望能指出你在大方向上的帮助。