Assume the following classes
// What I have created ...
public abstract class TaxServiceProvider<T, S>
where T : TaxServiceProviderConfig
where S : TaxServiceInfo
{
protected T Config { get; set; }
public abstract S GetTax(int zipCode);
}
public abstract class TaxServiceInfo { ... }
public abstract class TaxServiceProviderConfig { ... }
// What I want to create ...
public class SpecialTaxServiceProvider<T, S> : TaxServiceProvider<SpecialTaxServiceProviderConfig, SpecialTaxServiceInfo>
where T : SpecialTaxServiceProviderConfig
where S : SpecialTaxServiceInfo
{ ... }
public class SpecialTaxServiceInfo : TaxServiceInfo { ... }
public class SpecialTaxServiceProviderConfig : TaxServiceProviderConfig { ... }
where TaxServiceInfo and TaxServiceProviderConfig are used to support the TaxServiceProvider class.
I want to create a derived class SpecialTaxServiceProvder (non-abstract) from TaxServiceProvider that is also generic in the same way that TaxServiceProvider is and takes SpecialTaxServiceInfo and SpecialTaxServiceProviderConfig as the types.
I want to implement GetTax and Config in SpecialTaxServiceProvider so that GetTax returns type SpecialTaxServiceInfo and Config is of type SpecialTaxServiceProviderConfig
I would then create an additional class derived from SpecialTaxServiceProvider and classes derived from SpecialTaxServiceInfo and SpecialTaxServiceProviderConfig
public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, A_SpecialTaxServiceInfo>
{ ... }
public class A_SpecialTaxServiceProviderConfig : SpecialTaxServiceProviderConfig { ... }
public class A_SpecialTaxServiceInfo : SpecialTaxServiceInfo { ... }
where GetTax for this class returns type A_SpecialTaxServiceInfo and the Config for this class is of type A_SpecialTaxServiceProviderConfig
I've looked into covariance in C# and the syntax for generic typed classes but I'm not sure if what I'm trying to do is impossible in the language or I just don't know the proper way to set it up.
Change SpecialTaxServiceProvider and pass T and S to TaxServiceProvider:
public class SpecialTaxServiceProvider<T, S> : TaxServiceProvider<T, S>
where T : SpecialTaxServiceProviderConfig
where S : SpecialTaxServiceInfo
{
public override S GetTax(int zipCode)
{
return null;
}
}
Implement A_SpecialTaxServiceProvider and override GetTax:
public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, A_SpecialTaxServiceInfo>
{
public override A_SpecialTaxServiceInfo GetTax(int zipCode)
{
return null;
}
}
It prevents you from creating
public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, TaxServiceInfo>
with following error:
The type 'Project.TaxServiceInfo' cannot be used as type
parameter 'S' in the generic type or method
'Project.SpecialTaxServiceProvider'. There is no implicit
reference conversion from 'Project.TaxServiceInfo' to
'Project.SpecialTaxServiceInfo'.
I decided to use function delegates in my constructors to allow me to pass in factory methods to create my generic types.
Here is what I ended up with
// Tier 1 of my class hierarchy
public abstract class TaxServiceProvider<C, I>
where C : TaxServiceProviderConfig
where I : TaxServiceInfo
{
protected C Config { get; set; }
public abstract I GetTax(int zipCode);
}
public abstract class TaxServiceInfo {
public TaxServiceInfo(string param1, string param2, int param3, ect...) {
...
}
}
public abstract class TaxServiceProviderConfig { ... }
// Tier 2 of my class hierarchy
public class DerivedTaxServiceProvider<C, I> : TaxServiceProvider<C, I>
where C : DerivedTaxServiceProviderConfig
where I : DerivedTaxServiceInfo
{
protected Func<S, string, string, int, ect...> Factory;
public DerivedTaxServiceProvider (C config, Func<I, string, string, int, ect...> factory) {
Config = config;
Factory = factory;
}
public override I GetTax(int zipCode) {
...
I taxServiceInfo = Factory("param1", "param2", 3, ect...);
...
return I;
}
}
public class DerivedTaxServiceInfo : TaxServiceInfo {
public DerivedTaxServiceInfo(string param1, string param2, int param3, ect...)
: base(param1, param2, param3, ect...)
{ ... }
}
public class DerivedTaxServiceProviderConfig : TaxServiceProviderConfig { ... }
// Tier 3 of my class hierarchy
public class ConcreteTaxServiceProvider : DerivedTaxServiceProvider<ConcreteTaxServiceProviderConfig, ConcreteTaxServiceInfo> {
public ConcreteTaxServiceProvider(ConcreteTaxServiceProviderConfig config, Func<ConcreteTaxServiceInfo, string, string, int, ect...> factory) {
Config = config;
Factory = factory;
}
public override ConcreteTaxServiceInfo GetTax(int zipCode) {
return base.GetTax(zipCode);
}
}
public class ConcreteTaxServiceInfo : DerivedTaxServiceInfo {
public ConcreteTaxServiceInfo(string param1, string param2, int param3, ect...)
: base(param1, param2, param3, ect...)
{ ... }
public static ConcreteTaxServiceInfo CreateConcreteTaxServiceInfo(string param1, string param2, int param3, ect...) {
return new ConcreteTaxServiceInfo(param1, param2, param3, etc...);
}
}
public class ConcreteTaxServiceProviderConfig : DerivedTaxServiceProviderConfig { ... }
// Implementation of my class hierarchies
public void method() {
ConcreteTaxServiceProviderConfig() config = new ConcreteTaxServiceProviderConfig();
ConcreteTaxServiceProvider provider = new ConcreteTaxServiceProvider(config, ConcreteTaxServiceInfo.CreateConcreteTaxServiceInfo);
ConcreteTaxServiceInfo serviceInfo = provider.GetTax(99939);
}
So basically I have 2 levels of generic classes. The 2nd level Provider class overrides the base abstract method for "GetTax", but I didn't want it to return a concrete type because then I couldn't cleanly call that method in a class that inherited from it. I would have had to cast my derived ServiceInfo into a ConcreteServiceInfo type when calling "GetTax" in the concrete (3rd level) Provider.
As long as I have a constructor in an Info class that matches my delegate, no matter at what level of subclassing (below the 2nd level) that Info class is, I can feed it into my provider and use the 2nd level's Provider class GetTax method.
This factory method can look kind of ugly in the parameter lists for the Provider constructors and creating static methods of the Info classes seems awkward, but it does the trick!
Here is an SO question I consulted to come up with this solution : C# generics problem - newing up the generic type with parameters in the constructor
Related
Why i can't convert implementation of interface which concrete implement generic interface? I need for Cat, Dog etc own interface realisation.
public interface IMarker { }
public class ResultA : IMarker
{
}
public class ResultB : IMarker
{ }
public interface IService<T> where T : IMarker
{
public List<T> DoStuff();
}
public interface ICatService : IService<ResultA>
{ }
public interface IDogService : IService<ResultB>
{ }
public class CatService : ICatService
{
public List<ResultA> DoStuff()
{
return new List<ResultA>();
}
}
public class DogService : IDogService
{
public List<ResultB> DoStuff()
{
return new List<ResultB>();
}
}
public abstract class Animal
{
protected readonly IService<IMarker> _svc;
protected Animal(IService<IMarker> svc)
{
_svc = svc;
}
}
public class Cat : Animal
{
public Cat(ICatService svc) : base(svc)
{
}
}
public class Dog : Animal
{
public Dog(ICatService svc) : base(svc)
{
}
}
CS1503 Argument 2: cannot convert from 'ICatService' to 'IService'
I have DI for services i.e. :
services.AddTransient<ICatService, CatService>();
The reason for such behaviour is that in general case IService<ResultA> is not IService<IMarker> (basically I would argue the same cause for C# classes does not supporting variance which is for a pretty good reason - see more here and here).
In this concrete case everything can be fixed by making the interface covariant and leveraging the covariance of IEnumerable<T>:
public interface IService<out T> where T : IMarker
{
public IEnumerable<T> DoStuff();
}
public class CatService : ICatService
{
public IEnumerable<ResultA> DoStuff() => return new List<ResultA>();
}
public class Cat : Animal
{
public Cat(CatService svc) : base(svc)
{
}
}
But not sure that in your actual code you will be able to.
Or just make the base class generic (if this suits your use case):
public abstract class Animal<T> where T : IMarker
{
protected readonly IService<T> _svc;
protected Animal(IService<T> svc)
{
_svc = svc;
}
}
Original answer
CatService does not implement ICatService, i.e. the fact that ICatService inherits only IService<ResultA> does not mean that they are the same, C# is strongly-typed (mostly :-) language and compiler will consider those two interfaces being different ones (though related). You need either to make CatService to implement ICatService:
public class CatService : ICatService
{
// ...
}
Or register and resolve the IService<ResultA> interface (basically skipping intermediate interface at all):
services.AddTransient<IService<ResultA>, CatService>();
// ...
public Cat(IService<ResultA> svc) : base(svc){}
Given a set of services that each implement a generic interface where the type parameters all have a common base, how can I write a factory method to return an instance of one of these services? There seems to be no way to write a return type for the factory method.
Here is the sample code, with the problematic method at the bottom:
public abstract class Base
{
public int BaseProp { get; set; }
}
public class Derived1 : Base
{
public int DerivedProp1 { get; set; }
}
public class Derived2 : Base
{
public int DerivedProp2 { get; set; }
}
public interface IHandleStuff<T> where T : Base
{
T GetStuff();
void DoStuff(T t);
}
public class Service1 : IHandleStuff<Derived1>
{
public Derived1 GetStuff() => new();
public void DoStuff(Derived1 t){}
}
public class Service2 : IHandleStuff<Derived2>
{
public Derived2 GetStuff() => new();
public void DoStuff(Derived2 t){}
}
public class Consumer
{
public void DoStuff(Base t)
{
var service = GetServiceFor(t);
service.DoStuff(t);
}
private IHandleStuff<Base> GetServiceFor(Base t)
{
return t.BaseProp switch
{
1 => new Service1(), // Cannot convert expression type 'Service1' to return type 'IHandleStuff<Base>'
2 => new Service2(), // An explicit cast to IHandleStuff<Base> compiles but fails at runtime
_ => throw new ArgumentOutOfRangeException()
};
}
}
Update:
Someone pointed out in a comment (now deleted) that the issue in the above code can be resolved by making the DoStuff and GetServiceFor methods in the Consumer class generic. This works, but at some point in the real code we have to call into this from a non-generic method which knows only the base type. So the suggestion only defers the problem.
How do I force all derived classes of an interface to have a constructor with a signature? This doesn't work:
public interface Constructor<T> where T : Constructor<T>, new()
{
Constructor(T);
}
public interface IParameters
{
}
public interface IRule : Constructor<IParameters>
{
//IRule (IParameters); must exist
}
You can't, not via an interface. But you can sort of get at it with an abstract class. Similar to what the accepted answer here describes, try:
public abstract class MustInitialize<T>
{
public MustInitialize(T parameters)
{
}
}
public class Rule : MustInitialize<IParameters>, IRule
{
IParameters _parameters;
public Rule(IParameters parameters)
: base (parameters)
{
_parameters= parameters;
}
}
You can't force a specific constructor signature.
Even with an abstract class as demonstrated in Mark's answer, you can only force the constructor of the abstract class, but nothing is stopping the author of the derived class to do something like this:
public class Rule : MustInitialize<IParameters>, IRule
{
public Rule()
: base (new Parameters())
{
// Assuming Parameters is a class that implements the IParameters interface
}
}
However, you can force dependency injection by using method (setter) injection:
public interface IMethodInjection<T>
{
void Method(T injected);
}
I think you can design your base class like the following example:
public abstract class MyBase
{
private MyBase()
{
}
public MyBase(string a)
{
}
}
public class MyDerived : MyBase
{
public MyDerived(string a) : base(a)
{
}
}
You can even delete the private constructor if its not needed
I have some problem with delegates
My scheme of classes:
public interface IWorker<T> where T : IModel
{
T Do(T model);
T ReadyToWork(T model);
}
public abstract class Workers<T> : IWorker<T> where T : IModel
{
public abstract T Do(T model);
public abstract T ReadyToWork(T model);
}
Class to work! FirstModel : IModel
public class FirstWorker : Workers<ModelFirst>
{
public override ModelFirst Do(ModelFirst model)
{
return new ModelUserFirst();
}
public override ModelFirst ReadyToWork(ModelFirst model)
{
throw new NotImplementedException();
}
}
I can create many work specials classes, and to work with them I created one access point:
public class WorkPoint<T> where T : IModel
{
public static Func<T, T> Do { get; set; }
public static Func<T, T> ReadyToWork{ get; set; }
public WorkPoint(ModelFirst mod)
{
Do = FirstWorker.Instance().Do;
ReadyToWork= FirstWorker.Instance().ReadyToWork;
}
}
ok, and question. on moment assignments Do and ReadyToWork I catching error
Error CS0123 No overload for 'Do' matches delegate 'Func'
Whats wrong?
It's not the most elegant solution, but this works:
public class WorkPoint<T> where T : IModel
{
public Func<T, T> Do { get; }
public Func<T, T> ReadyToWork { get; }
protected WorkPoint(Func<T, T> f, Func<T, T> r)
{
Do = f;
ReadyToWork = r;
}
}
// Create a simple class like this for evert worker type you need
public class FirstWorkPoint : WorkPoint<ModelFirst>
{
private FirstWorkPoint() :
base(FirstWorker.Instance().Do, FirstWorker.Instance().ReadyToWork)
{ }
// Public method to get a new base instance
public static WorkPoint<ModelFirst> New() => new FirstWorkPoint();
}
This will add a bit of overhead due to the additional class created, but it should act as a workaround for that covariance issue being pointed out in the comments.
This way you could just do:
WorkPoint<ModelFirst> point = FirstWorkPoint.New();
Edit: Also, if you don't need that base Workers<T> class I'd say you could just get rid of it and have the actual worker classes directly inherit from your interface. After all, the base class doesn't provide any additional functionalities in the above code.
I have the following classes (some of them are in the PRISM framework and cannot be changed):
public abstract class NetworkEventBase<T> : CompositePresentationEvent<T> where T : NetworkEventPayload { }
public class NetworkEventPayload { }
public class TestEvent : NetworkEventBase<TestPayload> { }
public class TestPayload : NetworkEventPayload { }
// the following classes are PRISM classes:
public class CompositePresentationEvent<TPayload> : EventBase { }
public abstract class EventBase { }
Now I need to convert an instance of TestEvent to its base class NetworkEventBase inside a decorator for IEventAggregator. IEventAggregator looks like:
public interface IEventAggregator
{
TEventType GetEvent<TEventType>() where TEventType : EventBase, new();
}
Now in my decorator I try to convert like this:
public class MessageBusAdapterInjectorDecorator : IEventAggregator {
...
public TEventType GetEvent<TEventType>() where TEventType : EventBase, new()
{
var aggregatedEvent = this.eventAggregator.GetEvent<TEventType>();
var networkEvent = aggregatedEvent as NetworkEventBase<NetworkEventPayload>;
if (networkEvent != null)
{
networkEvent.MessageBusAdapter = this.messageBusAdapter;
}
return aggregatedEvent;
}
}
However, networkEvent is always null, even when the runtime type of aggregatedEvent is TestEvent.
You seem to hope that the class called NetworkEventBase<T> would be covariant in T. But generic classes can't be covariant in C# (generic interfaces can).
See other threads on this issue.