Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.Param2from the overriden ApplyChanges method in my Service class?

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.

like image 320
Adolfo Perez Avatar asked Dec 14 '22 08:12

Adolfo Perez


2 Answers

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. 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;
    }
}
like image 52
Benjamin Hodgson Avatar answered Dec 16 '22 20:12

Benjamin Hodgson


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. C# does not allow you to do that.

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.

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);
    }
}
like image 20
Domysee Avatar answered Dec 16 '22 21:12

Domysee