【问题标题】:How to override generic method with derived type in c#如何在 C# 中使用派生类型覆盖泛型方法
【发布时间】:2016-03-05 13:09:45
【问题描述】:

我有以下课程:

public interface IService
{
    void ApplyChanges<T>(T parameters) where T : ParamBase;
}

public class ServiceBase : IService
{
    public virtual void ApplyChanges<T>(T parameters) where T : ParamBase
    { }
}
public abstract class Service : ServiceBase
{
    public override void ApplyChanges<T>(T parameters) where T : ParamBase
    {
        Console.WriteLine(parameters.Param2);
        //Apply changes logic here...
    }
}

public abstract class ParamBase
{
    public string Param1 { get; set; }
}

public class ParamA : ParamBase
{
    public string Param2 { get; set; }
}

这里是我的测试主类:

void Main()
{
  var service = new Service();
  var paramA = new ParamA();
  paramA.Param2 = "Test2";
  service.ApplyChanges<ParamA>(paramA);
}

该实现有什么问题?如何从我的 Service 类中被覆盖的 ApplyChanges 方法访问 parameters.Param2

一般的想法是我有一个 ServiceBase,我希望它的派生类能够将不同的参数类型传递给 ApplyChanges 方法。

【问题讨论】:

  • 为什么你有一个什么都不做的 ApplyChanges 实现?这只是在示例中吗?
  • @Domysee 是的,这是我真实应用程序的简化版本。我只是想弄清楚通用继承和类型转换..
  • 你不能,除非你声明它是抽象的。
  • Param2 不是ParamBase 的一部分,所以通用的T:where ParamBaseParam2 一无所知。
  • 您没有正确使用泛型。我怀疑你犯了设计错误。你真正想做什么?

标签: c# generics inheritance


【解决方案1】:

我在这里做了一个飞跃,但听起来您打算拥有多个“服务”,每个“服务”都有一个关联的参数类型。

将类型参数放在方法 上,就像您在示例中所做的那样,强制该方法的所有实现都是多态的。 (技术术语是higher-rank quantification。)

相反,您应该将类​​型参数与服务本身相关联。这允许合约的给定实现声明它与哪个参数类型相关联。当您使用它时,我不会为基类或类型界限而烦恼。

interface IService<in T>
{
    void ApplyChanges(T param);
}

class Param1
{
    public int X { get; set; }
}
class Service1 : IService<Param1>
{
    public void ApplyChanges(Param1 param)
    {
        param.X = 123;
    }
}

class Param2
{
    public int Y { get; set; }
}
class Service2 : IService<Param2>
{
    public void ApplyChanges(Param2 param)
    {
        param.Y = 456;
    }
}

【讨论】:

    【解决方案2】:

    您不应该对方法覆盖施加更强的约束。被覆盖的方法应该扩展可能的输入参数并减少可能的结果。否则它会破坏Liskov Substitution Principle。 C# 不允许你这样做。

    也就是说,如果你真的想要它,你可以。不过,您不会在调用代码中收到编译器警告。如果您无法更改基类,请使用该解决方案。

    public class Service<TParam> : Service where TParam : ParamA
    {
        public override void ApplyChanges<T>(T parameters)
        {
            Console.WriteLine((parameters as TParam).Param2);
        }
    }
    

    更好的解决方案是将类型参数添加到ServiceBaseIService

    public interface IService<TParam>
       where TParam : ParamBase
    {
        void ApplyChanges(TParam parameters);
    }
    
    public abstract class ServiceBase<TParam> : IService<TParam>
        where TParam : ParamBase
    {
        public virtual void ApplyChanges(TParam parameters)
        { }
    }
    public class Service : ServiceBase<ParamA>
    {
        public override void ApplyChanges(ParamA parameters)
        {
            Console.WriteLine(parameters.Param2);
        }
    }
    

    【讨论】:

    • 执行此操作时出现此错误:重写和显式接口实现方法的约束是从基本方法继承的,因此无法直接指定它们
    • 我同意我的示例感觉像代码味道,我不想按照您的建议强制转换。还有哪些其他优雅的选项可以让我拥有一个可以传递不同派生参数类型的基本方法?
    • 感谢您的建议,这样更好。我必须公平地向 BenjaminHodgson 给出答案,因为他首先发布了一个非常相似的示例。
    【解决方案3】:

    真的,与其替换接口的泛型类型,不如使用“类型保护”更简洁。我说更简洁是因为接口的方法签名保持一致,实际上,还有什么比如何使用接口更重要? (显然小狗更重要)

    在方法本身中,您可以确保类型是所需的类型......

    public void Method(ParentRequest req){
    if(req is ChildRequest request){
    //Do logic here
    } else {
        throw new Exception($"request is of type {req.GetType().Name} and must be of type ParentRequest");
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-29
      • 2012-04-25
      • 2014-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多