参数约束 - 调用方法来检查约束中的类型

本文关键字:约束 类型 检查 调用 方法 参数约束 | 更新日期: 2023-09-27 18:37:17

我有一个带有泛型参数的方法:

internal void DoSomething<T>(T workWithThis)
{
}

我现在想将此方法限制为仅接受继承我想指定的几个接口之一的参数。但是我还没有找到一种方法。我想要的看起来像这样:

internal void DoSomething<T>(T workWithThis) where T : ISomething | ISomethingElse
{
}

显然这是行不通的,所以我尝试使用静态方法来检查 T 的类型:

public static bool CheckType(Type t)
{
return */check here*/
}
internal void DoSomething<T>(T workWithThis) where T : CheckType(typeof(T))
{
}

显然,这也行不通。问题是为什么?为什么编译器阻止我这样做,根据我的理解,它没有理由不工作

参数约束 - 调用方法来检查约束中的类型

为什么编译器阻止我这样做,根据我的理解,它没有理由不工作

编译器阻止你这样做,因为你试图做一些 C# 作为一种语言不支持的事情。您尝试使用的语法不符合 C# 规范第 10.1.5 节中的产品。

C# 作为一种语言根本不支持所需的方案。

现在至于为什么语言不允许这种灵活性 - 这归结为正常的平衡行为:

  • 有多少开发人员将从中受益以及受益多少
  • 其他开发人员理解更复杂的语言的额外负担
  • 设计语言功能、实现和测试语言功能所需的资源(主要在Microsoft内)

哦,当然,这不仅仅是 C# - CLR 也必须支持这样的限制,它至少会鼓励其他 CLR 语言也理解它。

我建议您通过两种单独的方法来解决此问题。请注意,它们不能只是泛型方法的重载,因为重载不能仅因泛型类型约束而异。如果您不介意对实现接口的值类型进行装箱,则可以重载:

internal void DoSomething(ISomething something)
{
}
internal void DoSomething(ISomethingElse somethingElse)
{
}

。尽管如果您传入一个值,其中表达式是实现两个接口的类型,则最终会出现重载歧义。

或者,只需为这两种方法指定不同的名称。

编译器必须在编译时验证所有约束,并且不能调用方法来执行此操作。

您可以在where约束中指定的唯一内容是:

  • new() - 需要一个无参数构造函数
  • class - 必须是引用类型
  • struct - 必须是值类型
  • SomeBaseClass
  • ISomeInterface
  • T : U - 必须是、继承自或实现其他泛型参数之一

有关更多信息,请参见 C# 编程指南 - 类型参数的约束。

至于为什么,你永远不必回答"我认为这没有理由起作用"。你必须从相反的方向开始,"为什么要这样做",然后提出足够合理和现实的场景和要求,使其值得实施。 参见Eric Gunnerson的负100分。

若要在代码中解决此问题,应从公共接口派生两个接口,并改为添加约束。

如果这两个接口没有任何共同点,那么我首先质疑实际添加约束的好处。

例如,如果你的代码要在与泛型类型/方法一起使用的对象上调用一个方法,那么显然两个接口必须对该方法是什么有相同的概念,而做到这一点的唯一方法是在公共基接口中定义该方法。两个接口碰巧声明了相同的方法或属性,具有相同的签名,但这并不能使其成为相同的方法。

话虽如此,您确定这里甚至需要泛型吗?

只声明两个方法,每个方法采用一个这样的接口怎么样?

internal void DoSomething(ISomething workWithThis)
internal void DoSomething(ISomethingElse workWithThis)

编译器使用泛型约束来确定泛型方法中对 T 可用的操作 - 因此允许 or 表达式对类型不安全。例如,您有两个接口IFirstISecond

public interface IFirst
{
    void First();
}
public interface ISecond
{
    void Second();
}
internal void DoSomething<T>(T workWithThis) where T : IFirst or ISecond
{
    //How to call this method if the type is ISecond
    workWithThis.First();
    //How to call this method if the type is IFirst
    workWithThis.Second();
}

您可以定义一个包含所有这些的空接口。

请记住,在C#接口中可以有多个继承。

例如:

public interface IHolder : ISomething, ISomethingElse 
{
}

和通用

internal void DoSomething<T>(T workWithThis) where T : IHolder
{
}