C#中的泛型,使用变量类型作为参数

145

我有一个通用方法

bool DoesEntityExist<T>(Guid guid, ITransaction transaction) where T : IGloballyIdentifiable;

如何使用以下方法:

Type t = entity.GetType();
DoesEntityExist<t>(entityGuid, transaction);

我一直收到以下编译错误:

找不到类型或命名空间名称“t”(您是否缺少使用指令或程序集引用?)

DoesEntityExist<MyType>(entityGuid, transaction);

这个方法完美地运行,但我不想每次都使用if指令来调用具有不同类型名称的方法。


你不能那样使用泛型(第一个例子)。 - Mitch Wheat
另外,为什么DoesEntityExist()方法需要一个事务参数? - Mitch Wheat
4
请问您需要翻译的内容是: Similar question at https://dev59.com/0XRB5IYBdhLWcg3wNk93 and https://dev59.com/03I95IYBdhLWcg3w8y2N - Jonas Elfström
@MitchWheat,也许他想检查实体是否存在于数据库中。 - Guillermo Gutiérrez
1
这不是一个重复的问题,它是另一个问题的不同方面 - 在其他问题中的答案不符合此问题的答案要求。 - G.Y
4个回答

192

泛型的重点在于提供编译时类型安全性,这意味着类型需要在编译时知道。

您可以使用反射调用具有仅在执行时已知的类型的泛型方法:

// For non-public methods, you'll need to specify binding flags too
MethodInfo method = GetType().GetMethod("DoesEntityExist")
                             .MakeGenericMethod(new Type[] { t });
method.Invoke(this, new object[] { entityGuid, transaction });

糟糕。

你是否能够将你的调用方法改成通用类型,并将类型参数作为类型参数传递,将决策推高一级?

如果你能够给我们更多关于你正在做的事情的信息,那会很有帮助。有时候你可能需要像上面那样使用反射,但是如果你选择正确的时机进行操作,你可以确保你只需要这样做一次,并让下面的所有内容正常使用类型参数。


21
我认为这个回答中最重要的是 ick。还有 编译时类型安全 - Fredrik Mörk
12
问题在于有时是必要的。尽可能避免使用它,因为它很丑陋...但有时确实需要它。 - Jon Skeet
1
@MoslemBenDhaou:不行,因为这里没有实例可用于类型推断。 - Jon Skeet
2
@MoslemBenDhaou:好吧,你可以通过使用“dynamic”推断类型的中间通用方法来进行。如果这不足够信息,请提出一个新问题 - 在评论中追求它并不理想,因为它与此问题有足够的不同。 - Jon Skeet
1
@JonSkeet:这里有一个新问题:https://dev59.com/WWQn5IYBdhLWcg3w5aey(谢谢!) - Moslem Ben Dhaou
显示剩余3条评论

40

解决这个问题的一种方法是使用隐式转换:

bool DoesEntityExist<T>(T entity, Guid guid, ITransaction transaction) where T : IGloballyIdentifiable;

这里需要这样调用:
DoesEntityExist(entity, entityGuid, transaction);

更进一步,您可以将其转换为扩展方法(需要在静态类中声明):

static bool DoesEntityExist<T>(this T entity, Guid guid, ITransaction transaction) where T : IGloballyIdentifiable;

如下方式进行调用:

entity.DoesEntityExist(entityGuid, transaction);

这太棒了!有什么缺点吗?似乎比使用反射更优雅地解决了原始问题? - Sonic Soul
1
只有在声明的类型足够时才有效。如果实体声明为对象,该怎么办?JonSkeet的解决方案创建了一个通用版本,其中T是真实类型,而不是声明的类型。 - Grigory
我不知道使用 T 约束可以让你在调用方法时绕过类型参数。我一直在尝试类似于 Sort<typeof(input)>(input) 的东西,但其实只需要写成 Sort(input) 就可以了! - Bret

12

我不确定我是否正确理解了您的问题,但您可以以以下方式编写代码:

bool DoesEntityExist<T>(T instance, ....)

您可以按照以下方式调用该方法:

DoesEntityExist(myTypeInstance, ...)

这样,您就不需要显式地编写类型,框架会自动从实例中获取类型。


5
你不能按照你所描述的方式使用它。通用类型的重点在于,虽然在“编码时间”你可能不知道它们,但是编译器需要能够在编译时解决它们。为什么?因为在内部,编译器将会为每个“开放”的通用类型的不同用法创建一个新的类型(有时称为封闭的通用类型)。
换句话说,在编译后,
DoesEntityExist<int>

是一种不同类型的

DoesEntityExist<string>

这就是编译器如何实现编译时类型安全检查的方式。
对于您描述的情况,您应该将类型作为可以在运行时进行检查的参数传递。
另一个选择是使用反射从开放类型创建闭合类型,尽管我认为这只适用于极端特殊情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接