【问题标题】:How to constrain the argument(s) of a method to the child class type, when the method is defined in a base class? [duplicate]当方法在基类中定义时,如何将方法的参数限制为子类类型? [复制]
【发布时间】:2021-07-21 18:15:31
【问题描述】:

假设我有

class Program
{
    static void Main(string[] args)
    {
        Apple myApple = new Apple(5);
        Orange myOrange = new Orange(5);
        bool isSame = myApple.Compare(myOrange);
    }
}

class Fruit
{
    private readonly int _Size;
    public Fruit(int size)
    {
        _Size = size;
    }
    public bool Compare<T>(T otherFruit) where T : Fruit
    {
        if (this._Size == otherFruit._Size) return true;
        return false;
    }
}

class Apple : Fruit
{
    public Apple(int size) : base(size) { }
}

class Orange : Fruit
{
    public Orange(int size) : base(size) { }
}

而且我不想将苹果与橙子进行比较。我知道我可以

public bool Compare<T>(T otherFruit) where T : Fruit
{
    if (this.GetType() != otherFruit.GetType()) return false;
    if (this._Size == otherFruit._Size) return true;
    return false;
}

但感觉我应该能够通过类型约束来做到这一点。

我可以做到这一点(即使它很丑),而且它看起来可以工作,但没有达到我的预期

public bool Compare<T>(T otherFruit) where T : Fruit
{
    return Compare(otherFruit, this);
}

public static bool Compare<T>(T fruit1, T fruit2) where T : Fruit
{
    if (fruit1._Size == fruit2._Size) return true;
    return false;
}

我怎样才能使我在基类中定义的比较方法在子类中运行时只允许与子类匹配的参数类型? (因此理想情况下,在编译时您只能将 Apples 与 Apples 以及 Oranges 与 Oranges 进行比较)?

有趣的是,如果我用扩展方法替换实例比较方法,它会做我想要/期望的(我得到编译时检查,我只是比较苹果和苹果)

static class FruitComparer
{
    public static bool Compare<T>(this T currentFruit, T otherFruit) where T : Fruit
    {
        return Fruit.Compare(currentFruit, otherFruit);
    }
}

但同样,似乎应该有一种更简单的方法来做到这一点......也许我错过了一些非常明显的东西?

编辑:这确实有效(你会得到编译时检查,只有 Apples 可以与 Apples 进行比较),但前提是直接调用

public static bool Compare<T, U>(T fruit1, U fruit2) where T:Fruit,U where U:Fruit
{
    if (fruit1._Size == fruit2._Size) return true;
    return false;
}

奇怪的是,如果您从我的实例比较方法中调用该静态方法,您不会得到编译时检查 - 它可以让您将 Apples 与 Oranges 进行比较。

EDIT2:这是根据链接的问题/彼得的 cmets 实现的方法

class Fruit<X>
{
    private readonly int _Size;
    public Fruit(int size)
    {
        _Size = size;
    }
    public bool Compare<T>(Fruit<T> otherFruit) where T: Fruit<X>
    {
        return true;
    }
}

class Apple : Fruit<Apple>
{
    public Apple(int size) : base(size) { }
}

class Orange : Fruit<Orange>
{
    public Orange(int size) : base(size) { }
}

而且它似乎确实有效,但感觉就像是为了获得类型约束而添加了很多额外的东西,而且明显的自我引用对我来说感觉很奇怪。也许这是最好的。我将看看是否可以找到有关此模式的更多信息。

【问题讨论】:

  • 你应该使基类泛型,其中类型参数是派生类型的类型。例如。 class Apple : Fruit&lt;Apple&gt;。如果没有具体说明它所期望的哪个派生类型,则不允许代码使用基类,确保您只比较苹果和苹果、橙子和橙子,而不是苹果和橙子。查看副本。
  • 谁结束了这个问题 - 虽然这个问题似乎在标题中提出了同样的问题,但它实际上有些不同并且可以说更复杂。当然给出的答案更复杂,我在这里提出的扩展方法可能性。
  • 问题的上下文略有不同,但基本相同,答案相同。如果你不能限制调用者在处理基类时知道他们在处理什么水果,那么就没有办法保证兼容的比较。如果你可以约束调用者,那么通过将泛型类型参数从方法移动到类来实现你想要的,就像在副本中一样。
  • @PeterDuniho 这种模式——通用基类的东西——很有趣。但是,我没有找到任何参考资料 1) 证明答案是最正确的(即“我如何正确...”)或支持您的假设“您应该”。当然,看起来我可以让它做我所追求的,但它似乎和我探索的其他选择一样(不必要地)复杂。此外,静态比较方法可以做我所追求的这一事实似乎暗示实例方法中缺少某些东西。
  • 它被称为Curiously Recurring Template Pattern,它是目前在C#中强制执行的唯一方法,因为没有where T : this约束

标签: c# inheritance


【解决方案1】:

涉及两个参数和两个类型参数。您需要确保它们都是水果并且它们是同一类型。

public bool CompareFruit<T1, T2>(T1 fruit1, T2 fruit2) where T1 : Fruit, T2
                                                       where T2 : Fruit
{
    return fruit1.Size == fruit2.Size;
}

【讨论】:

  • 这对我来说似乎不是很有用或安全。当有一个中间类StoneFruit : Fruit 并且调用者调用CompareFruit&lt;Plum, StoneFruit&gt;(fruit1, fruit2) 但事实证明fruit2 实际上是一个Apricot 时会发生什么?您仍然不能保证比较是在相同类型之间进行的。
  • 我实际上确实尝试过 public static bool Compare(T fruit1, Ufruit2) where T:Fruit,U where U:Fruit { if (fruit1._Size == fruit2._Size)返回真;返回假;并且当您自己调用它时有效,但如果您从实例比较中调用它,它可以让您将 Apples 与 Oranges 进行比较
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
  • 1970-01-01
  • 2013-02-03
相关资源
最近更新 更多