简体   繁体   English

如何在C#中使用派生类型重写泛型方法

[英]How to override generic method with derived type in c#

I have the following classes: 我有以下课程:

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

Here my test main class: 这是我的测试主要课程:

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

What is wrong with that implementation? 该实现有什么问题? How can I access parameters.Param2 from the overriden ApplyChanges method in my Service class? 如何从Service类中重写的ApplyChanges方法访问parameters.Param2

The general idea is that I have a ServiceBase and I want to be able for its derived classes to pass different parameter types to the ApplyChanges method. 总体思路是,我有一个ServiceBase,并且希望它的派生类能够将不同的参数类型传递给ApplyChanges方法。

I'm making a leap here, but it sounds like you intend to have multiple "services", each with an associated parameter type. 我在这里取得了飞跃,但这听起来像您打算拥有多个“服务”,每个“服务”都具有关联的参数类型。

Putting a type parameter on the method , as you have done in the example, forces all implementations of that method to be polymorphic. 如在示例中所做的那样,将类型参数放在方法上 ,将强制该方法的所有实现都是多态的。 (The technical term for this is higher-rank quantification .) (此术语的技术术语是更高级别的量化 。)

Instead, you should associate the type parameter with the service itself. 相反,您应该将type参数与服务本身相关联。 This allows a given implementation of the contract to declare which parameter type it's associated with. 这允许合同的给定实现声明与之关联的参数类型。 While you're at it, I wouldn't bother with the base classes or the type bounds. 当您使用它时,我不会理会基类或类型界限。

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

You shouldnt impose stronger constraints for method overrides. 您不应该对方法重写施加更严格的约束。 An overridden method should expand the possible input parameters and reduce the possible outcomes. 覆盖的方法应扩展可能的输入参数并减少可能的结果。 Otherwise it breaks Liskov Substitution Principle . 否则,它将破坏Liskov替代原则 C# does not allow you to do that. C#不允许您这样做。

That said, if you really want it, you could. 就是说,如果您真的想要,可以。 You won't get compiler warnings in the calling code though. 但是您不会在调用代码中收到编译器警告。 Use that solution if you cannot change the base class. 如果您不能更改基类,请使用该解决方案。

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

A better solution would be to add a type parameter to ServiceBase and IService . 更好的解决方案是向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);
    }
}

Really, instead of replacing the interface's generic type, it is cleaner to use a "Type Guard". 实际上,与其替换接口的泛型类型,不如使用“类型防护器”更干净。 I say cleaner because the interface's method signature stays consistent, and really, what's more important than how your interface is used? 我说的更干净是因为接口的方法签名保持一致,实际上,比使用接口更重要的是什么? (Obviously puppies are more important) (显然,小狗更重要)

Within the method itself, you can make sure that the type is the one desired as such... 在方法本身内,您可以确保类型是所需的一种,例如...

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM