如何在c#中使用派生类型覆盖泛型方法

时间:2016-03-05 13:09:45

标签: c# generics inheritance

我有以下课程:

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类中的覆盖parameters.Param2方法访问ApplyChanges

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

3 个答案:

答案 0 :(得分:5)

我在这里进行了一次飞跃,但听起来你打算拥有多个&#34;服务&#34;,每个服务都有一个相关的参数类型。

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

相反,您应该将type参数与服务本身相关联。这允许给定的合同实现声明它与哪个参数类型相关联。在你了解它的同时,我也不会为基类或类型界限烦恼。

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;
    }
}

答案 1 :(得分:4)

您不应对方法覆盖施加更强的约束。重写方法应扩展可能的输入参数,并减少可能的结果。否则它会中断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);
    }
}

答案 2 :(得分:0)

确实,代替替换接口的泛型类型,使用“类型防护器”更为干净。我说的更干净是因为接口的方法签名保持一致,实际上,比使用接口更重要的是什么? (显然,小狗更重要)

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

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");
    }
}