【问题标题】:Abstract method to be overridden with concrete type用具体类型覆盖的抽象方法
【发布时间】:2016-01-26 23:15:28
【问题描述】:

也许这是一个愚蠢的问题。但是,我不明白我错过了什么。

给定以下类定义

public abstract class AbstractBaseClass
{
    public abstract void Create(AnotherAbstractClass param1);
}

Weras AnotherAbstractClass 已定义

public abstract class AnotherAbstractClass
{
}

具体实现

public class AnotherConcreteImplementation : AnotherAbstractClass
{
}

我希望能够覆盖 Create 方法以使用具体类型:

public class ConcreteImplementation : AbstractBaseClass
{
    public override void Create(AnotherConcreteImplementation param1) <-- There is no suitable method for override
    {
        // param1 is an instance of the concrete implementation
    }

    public override void Create(AnotherAbstractClass param1) <-- this is working but I'll have to cast on each implementation
    {
        // param1 is an instance of the abstract class and needs a cast
    }
}

这根本不可能,还是我不知道有什么方法?也许使用泛型?

编辑 #1(添加更多上下文)

我试图实现/强制在具体实现中只有一个参数有效。 把它想象成一个数据库层。 Create 方法将在数据库中创建一个新条目。由于每个表都有不同的值,create-parameter 也有。 可以使用AnotherAbstractClass 的任何具体实现来调用内部铸造的气味(在我看来)。

public class AddressTable : AbstractBaseClass
{
    public override void Create(AnotherAbstractClass param1)
    {
        // cast to concrete instance
        var casted = (ConcreteAddressCreate)param1;
    }
}

public class CityTable : AbstractBaseClass
{
    public override void Create(AnotherAbstractClass param1)
    {
        // cast to concrete instance
        var casted = (ConcreteCityCreate)param1;
    }
}

拥有AddressTable 的实例我可以调用

addressIntance.Create(new ConcreteAddressCreate()); // would be okay

另一方面,我可以称之为

addressIntance.Create(new ConcreteCityCreate()); // would be okay but will fail at runtime with InvalidCastException

编辑#2(附加信息)

以后还应该可以使用更多抽象方法扩展AbstractBaseClass 类。 因此,对我来说,更可能使用泛型方法而不是具体的类实现,每个方法要实现 200 个泛型参数。

【问题讨论】:

  • 大声笑,每个人都用一个通用代码 sn-p 回答你,没有任何想法或真正的意图来帮助你,只是为了放弃一些观点。基本上你问的问题是可能的,但这是一个巨大的代码气味,真正的问题是你为什么认为你需要这个?你能给我们更多的背景信息吗,因为我相信有更好的类设计来实现你真正需要的东西。
  • @dustinmoris 我不回答只是废弃一些要点,我有很多这些,但对它们没有用处。我只是想向 OP 展示如何快速使用泛型来解决这个特定问题。我同意这看起来像代码味道。
  • @dustinmoris 这看起来不像是代码味道,而且没有人在“为积分报废”(无论如何,我很容易在积分上封顶,很有可能我真的没有积分回答这个问题)。
  • @dustinmoris 查看我更新的问题(试图澄清我的预期目标)
  • @BenjaminGruenbaum 作者询问如何通过重载将抽象类型转换为具体类型。对我来说这是一种代码味道,因为第一个问题应该是为什么他不能直接在抽象类型上工作?

标签: c# generics inheritance design-patterns abstract-class


【解决方案1】:

它违反了 Liskov 替换原则,所以你不能这样做是完全合理的。也就是说,你不能像这样免费“拥有”协方差:

AbstractBaseClass bcl = new ConcreteImplementation();
bcl.Create(new DifferentImplementationWithoutSecondAbstract());

AbstractBaseClass 定义的合同使Create 必须与传入的AbstractBaseClass 的任何实现一起工作 - 如果您对可以传入的内容进行限制,则您违反了它定义的合同.

如您所料 - 您可以使用泛型:

// notice the recursive definition, we require the generic parameter
// to be a generic parameter of itself - allowing AbstractBaseClass
// to not be aware of its subclasses like in the other answers.
public abstract class AbstractBaseClass<T> where T : AbstractBaseClass<T>
{
    public abstract void Create(T param1);
}

public class Concrete : AbstractBaseClass<Concrete>
{
    public override void Create(Concrete param1)
    {
        Console.WriteLine("Hello!");
    }
}

【讨论】:

  • 这不是他要求的。您的示例中只有 1 个抽象类,但原始问题有 2 个抽象类。
  • @dustinmoris 我可以添加public abstract class SecondAbstractClass&lt;T&gt; where T : SecondAbstractClass&lt;T&gt; { public abstract void Create(T param1); },然后让Concrete 扩展它。它不会为答案增加任何实质内容 - OP 的问题并不是 实际上 由第二个抽象类引起的。这是因为AbstractBaseClass 中的合同对实施者的约束。
  • T 应该派生自 AnotherAbstractClass,而不是 AbstractBaseClass。这样做并不违反里氏替换原则。
  • @RobLyndon 如果你在谈论 OP 的问题 - 它确实是因为它违反了“先决条件不能在子类型中得到加强”。 (或者,甚至更直接:子类型中方法参数的逆变) - 通过强制参数为子类型,我们违反了合同。
  • AbstractBaseClass 和参数AnotherAbstractClass 之间似乎没有继承关系。
【解决方案2】:

是的,您可以使用泛型来做到这一点:

public abstract class AbstractBaseClass<T>
    where T : AnotherAbstractClass
{
    public abstract void Create(T param1);
}

public class ConcreteImplementation : AbstractBaseClass<AnotherConcreteImplementation>
{
    public override void Create(AnotherConcreteImplementation param1)
    {
    }
}

【讨论】:

  • 这将强制AbstractBaseClass 循环注意其子类。
  • 为什么在课堂上通用。您可以将 Create 设为泛型。所以这个public abstract void Create&lt;T&gt;(T param1) where T: AnotherAbstractClass; 并像这样覆盖public override void Create&lt;T&gt;(T param1) { }
  • @M.kazemAkhgary - 它们不是一回事,使方法通用需要你处理AnotherAbstractClass 的子类,把它放在类上只需要你处理一个。
  • @Benjamin 定义“注意”。此代码没有引入 OP 示例所没有的新依赖项。可能Create() 的参数在类中被使用,所以OP 可能需要T _instance 字段或调用AnotherAbstractClass 中定义的成员。
  • @M.kazemAkhgary 和 public override void Create&lt;T&gt;(T param1) { } 问题在于 param1 是基本类型,而不是具体类型,我仍然需要转换
【解决方案3】:

泛型确实是这样做的方法。

public abstract class AbstractBaseClass<TDerivedClass> where TDerivedClass : AnotherAbstractClass
{
    public abstract void Create(TDerivedClass param1);
}

然后你可以这样做:

public class ConcreteImplementation : AbstractBaseClass<AnotherConcreteImplementation>
{
    public override void Create(AnotherConcreteImplementation param1) // Works because TDerivedClass = AnotherConcreteImplementation 
    {
        ...
    }
}

【讨论】:

  • 这就像 CodeCaster 不久前的回答 - 并且遇到了同样的问题,即要求该类了解其子类 - 这不是必要的要求 - 请参阅我的关于如何防止它的回答.
  • @BenjaminGruenbaum 您能否详细说明该类需要如何了解其子类?似乎该类需要了解 another 抽象类型 的子类
  • 需要注意AnotherAbstractClass
  • 而AnotherAbstractClass对AbstractBaseClass没有依赖,那么子类的意识在哪里呢?
  • public abstract class AnotherAbstractClass { }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-01
  • 1970-01-01
  • 2020-11-09
  • 1970-01-01
  • 1970-01-01
  • 2017-09-26
相关资源
最近更新 更多