【问题标题】:C# Generics lower bound constraint "where MySubClass : T" (java's "super")C# 泛型下界约束“where MySubClass : T”(java 的“super”)
【发布时间】:2014-02-16 16:33:47
【问题描述】:

我想知道 C# 是否有等效于 Java 的 <X super MySubClass> 泛型约束。

要指定上限,可以使用class Foo<T> where T : MySuperClass { ... },但我们如何指定泛型参数的下限?


有一些方法可以获得类似的结果,但我还没有找到完美的方法:

  1. 使用第二个通用参数——但调用者可以指定实际下限的子类。

    public class Foo<T, TLowerBound>
        where TLowerBound : MySubClass
        where TLowerBound : T
    {
        ...
    }
    
  2. 这有时用于扩展方法,因此扩展方法的参数U 被限制为类参数T 的超类。

    public static class Extensions {
        public static void Method<T, U>(this Foo<T> self, U someU) where T : U {
            self.ValueOfTypeT = someU;
        }
    }
    
  3. 在接口上使用变量,但我不确定这是否可用于指定泛型参数的下限。

【问题讨论】:

  • 不,基本上 C# 没有任何等价物。
  • @ChrisShain 啊,是的,我前段时间读过这个问题及其答案,最终忘记了它们的存在——谷歌搜索了两个小时后没有找到它们:(。

标签: c# java generics lower-bound


【解决方案1】:

我刚刚遇到了同样的问题。选项 2(使用扩展方法)工作得很好,直到您需要下限为虚拟的方法(因此根据对象的动态类型进行调度)。如果您需要,这里有一个使用选项 3 的可行解决方案(界面变化,加上众所周知的访问者模式)。

为了实现等价

public class A<T> // an argument to the generic method
{
}

public class B<S>
{
    public virtual R Fun<T>(A<T> arg) where S : T // illegal in C#/CLR
    {
        ...
    }
}

public class C<S> : B<S>
{
    public override R Fun<T>(A<T> arg)
    {
    }
}

您执行以下操作。首先,您为要执行的操作定义一个接口(我们将在这里使用访问者模式,因此必须为每种类型提供一个单独的方法来覆盖 Fun):

public interface IFun<in T>
{
    R Fun<S>(B<S> self) where S : T;
    R Fun<S>(C<S> self) where S : T;
}

注意,泛型参数T 仅用作约束,因此接口可以相对于它是逆变的。我们现在使用它,让BC 被操作“访问”:

public class B<S>
{
    public virtual R Perform(IFun<S> fun)
    // contravariant, any IFun<T> with S : T will be accepted
    {
        return fun.Fun(this);
    }
}

public class C<S> : B<S>
{
    public override R Perform(IFun<S> fun)
    {
        return fun.Fun(this);
    }
}

为了使用参数A&lt;T&gt; 实际执行操作,请将其包装在实现接口的结构/类中:

public struct TheFun<T> : IFun<T>
{
    public A<T> arg;

    R IFun<T>.Fun<S>(B<S> self)
    {
        ... body of B<S>.Fun(A<T> arg) ...
    }

    R IFun<T>.Fun<S>(C<S> self)
    {
        ... body of C<S>.Fun(A<T> arg) ...
    }
}

为了结束,你引入一个扩展方法,就像在选项 2 中一样:

public static class Extensions
{
    public static R Fun<S,T>(this B<S> self, A<T> arg) where S : T
    {
        return self.Perform(new TheFun<T> { arg = arg });
    }
}

完成。它可以工作,无需进行一次强制转换或类型检查。主要缺点是:

  • 它非常复杂(尽管代码大小比预期的要长,只是一个常数因素)并且可能会让阅读您的代码的人讨厌您
  • 实现已从 BC 移至 TheFun,因此必须使 BC 的任何必需成员都可以在此处访问

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多