I have multiple classes which extend a generic abstract base class:
public class BaseDetails {}
public class ADetails : BaseDetails {}
public class BDetails : BaseDetails {}
public abstract class Base<T> {}
public class A : Base<ADetails> {}
public class B : Base<BDetails> {}
This is all working, what I am struggling with is the dependency injection for registering many implementations of the generic abstract base class, so what I would like is:
IEnumerable<Base<BaseDetails>> _baseClasses;
I have tried to DI this like so but with no luck:
builder.RegisterMany(new[] { Assembly.Load(nameof(Program)) }, type => type.IsAssignableTo(typeof(Base<>)));
I feel like I am missing something on the DI side of things, any help is much appreciated!
Welcome to the world of covariance and contravariance. While IEnumerable<T> is covariant, your Base<T> class isn't, because classes can't have any variance applied to them. Only interfaces and delegates can. This means that the following code does not compile:
Base<BaseDetails> b = new A();
This code above is an important exercise to do, because when it comes to generics, DI Containers can easily cause you to be mislead in thinking that the problem is in the DI Container. Always try to reconstruct the problem by hand-wiring your object graphs without the use of the DI Container. For instance:
IEnumerable<Base<BaseDetails>> _baseClasses = new Base<BaseDetails>[]
{
new A(), // <- compile error here
new B(), // <- compile error here
};
Again, this code will not compile in C#:
CS0029 Cannot implicitly convert type 'A' to 'Base'
To make this work, you will have to change Base<T> to an interface and make its T generic argument covariant:
public interface Base<out T> { }
Something like this:
builder.RegisterMany(new[] { Assembly.Load(nameof(Program)) },
type => type.IsGeneric && type.GetGenericTypeDefinition() == typeof(Base<>)));
Related
In .NET Core C#
I' trying something like this:
(IInterface<IParameter>)instance
Where instance is new Implementation<Parameter>()
And Implementation : IInterface & Parameter : IParameter
The issue is with the casting of the generic parameter. When I provide Parameter instead of IParameter it works but at compile time there is no way to know which type that implements IParameter will be used. All of these objects will be created via reflection.
So is there any way this cast works? Or some other way to implement this like providing no generic type parameter like you can with typeof.
EDIT Thanks to Ziriax
A Fully Working Example:
interface IInterface
{
void Run(TInput input);
}
abstract class AbstractClass<TInput> : IInterface
where TInput : IParameter
{
public abstract void Execute(TInput input);
public void Run(IParameter input)
{
Execute((TInput)input);
}
}
interface IParameter {}
class Implementation : AbstractClass<Parameter>
{
public void Run(Parameter input)
{
}
}
class Parameter : IParameter {}
class Program
{
static void Main()
{
object instance = new Implementation();
var castInstance = (IInterface) instance;
castInstance.Run(new Parameter());
}
}
Why don't you add a non-generic interface too:
interface IInterface
{
void Run(IParameter input);
}
And then let your generic interface extend this non-generic one.
Obviously your implementations should cast the IParameter, someone needs to cast it... You could make an abstract base class that does this for you, so not every implementation has to do this.
You might also be interested in the double dispatch pattern, although I'm not sure this will work in your case.
As you have it now, this cannot work. Your Implementation class implements IInterface<Parameter>, so its Run method only accepts a parameter of the concrete Parameter type, whereas the IInterface<IParameter> requires that its Run method accepts an instance of any type that implements IParameter.
If the type of cast you're trying to do were allowed, I could define a different class that implements IParameter, e.g.:
public class DifferentParameter : IParameter { ... }
And then do:
castInstance.Run(new DifferentParameter());
But your Implementation's Run method can't take DifferentParameter!
.NET therefore prevents you from performing the cast itself.
There are situations in which this kind of cast is allowed - if your interface were instead to be defined as:
interface IInterface<out TOutput>
where TOutput : IResult
{
TOutput Run();
}
By making the generic parameter out, it makes the interface covariant. This restricts the use of the type parameter as the result of method calls, but for covariant interfaces, casts like yours are allowed.
You can find plenty of documentation on both covariance and contravariance in the .NET documentation.
I am trying to attempt something such as
void Main()
{
Temp<Bar> Test = new Foo<InheritedBar>();
}
abstract class Temp<T> where T : Bar
{
}
class Foo<T> : Temp<T> where T : Bar
{
}
abstract class Bar
{
}
class InheritedBar : Bar
{
}
The cast does not work, with the error Cannot implicity convert type Foo<InheritedBar> to Temp<Bar>.
However,
Temp<InheritedBar> Test = new Foo<InheritedBar>();
and
Temp<Bar> Test = new Foo<Bar>();
Both work. Why even though InheritedBar inherits from Bar, it can't be cast to it through generics?
I am using the generic type in a wpf Page, which can not be created as a generic so I can't pass T as its type. I only want the functionality at the time of this of Temp, not any of the derived versions functionality. Is there a better way to do this?
The concept you are attempting to utilize is covariance (full explanation on MSDN)
The short answer is that you need to use mark your generic parameter with out; but you can only do that on an interface (changing from an abstract class in your case). Also, depending on your method parameters and return values, you may not be able to mark your interface as covariant.
interface Temp<out T> where T : Bar
{
}
I have an interesting situation where I'd like to use a base class utilising a type parameter to implement an interface and also keep things DRY with inheriting classes.
public interface ICalculator
{
void Process(ICalculationModel calculationModel);
}
public abstract class CalculatorBase<T> :ICalculator where T : ICalculationModel
{
// Compiler moans that Process(ICalculationModel calculationModel) isn't implemented
public abstract void Process(T calculationModel);
}
public class PipeworkInspections : CalculatorBase<GasSafetyModel>
{
public override void Process(GasSafetyModel documentModel){
//snip
}
}
Is there something i'm missing with the generic 'where' clause or something? In my head this should work. Or does the compiler need EXACTLY the same implementation as the interface definition?
I can't easily move the type parameter into the ICalculator as there are a lot of places that it is used without any requirement for the generic.
That's cleared things up. Thanks for the info. Now obviously a solution is to make the interface take the type parameter. However ICalculator's are used in a number of places and are referenced just as ICalculator I now get compiler errors if I omit the type parameter in Interfaces that refer to ICalculator... Is there a way to architect this that should work!?
In my head this should work.
The problem then is in your head! :-) This should not work. Let's see why.
interface ICage
{
void Enclose(Animal animal);
}
class ZooCage<T> : ICage where T : Animal
{
public void Enclose(T t) { ... }
}
...
var giraffePaddock = new ZooCage<Giraffe>();
var cage = (ICage)giraffePaddock;
var tiger = new Tiger();
icage.Enclose(tiger);
And now there is a tiger in the giraffe paddock, and life is good for the tiger but bad for the giraffes. That's why this is illegal.
Or does the compiler need EXACTLY the same implementation as the interface definition?
The member which implements an interface member must exactly match the signature of the implemented method. For example, you cannot use return type covariance:
interface I
{
Animal GetAnimal();
}
class C : I
{
public Giraffe GetAnimal() { ... } // not legal.
}
The contract requires an animal; you provide a giraffe. That should work, logically, but this is not legal in C#. (It is in C++.)
See any of the many questions on this site about return type covariance for the reasons why.
Similarly for parameter type contravariance:
interface I
{
void PutMammal (Mammal mammal);
}
class C : I
{
public PutMammal(Animal animal) { ... } // not legal.
}
Again, this is logically sensible; the contract requires that you take a mammal, and this takes any animal. But again, this is not legal.
There are some covariant and contravariant operations in C#; see any of numerous questions on those topics on this site, or browse the covariance and contravariance articles on ericlippert.com or my previous msdn blog.
If this worked then you'd be able to say something like this:
PipeworkInspections pipeworks = new PipeworkInspections();
ICalculator calculator = pipeworks;
NuclearPowerSafetyModel nuclearModel = new NuclearPowerSafetyModel();
calculator.Process(nuclearModel); // <-- Oops!
That's probably not what you wanted...
Your interface says any class implementing it will provide this method:
void Process(ICalculationModel calculationModel);
Now obviously PipeworkInspections does not. It does not have a method Process that accepts any ICalculationModel. IT only has a method accepting specific implementations of ICalculationModel. So your compilation fails.
Yes, you need the exact implementation.
As an alternative you can make interface and Process method generic if it works for you:
public interface ICalculator<T> where T : ICalculationModel
{
void Process(T calculationModel);
}
public abstract class CalculatorBase<T> : ICalculator where T : ICalculationModel
{
public abstract void Process(T calculationModel);
}
I agree with Eric Lippert's response: you can't. And he explained in a very good way why this happens.
If you really want to do this, you can add the following to your abstract class, and it will compile:
void ICalculator.Process(ICalculationModel calcMod)
{
Process((T)calcMod);
}
But you need to know what you are doing, otherwise you might have some InvalidCastException at runtime.
Can something like this be accomplished using C#?
public abstract class BaseClass
{
public abstract IInterface<T> CreateEditor() where T: the_actual_type_of_this_instance;
}
Example usage:
var instance = new DerivedClass();
var editor = instance.CreateEditor(); // IInterface<DerivedClass>
No, you can't do that - partly because it wouldn't make sense at compile time. Consider a slight change to your code:
BaseClass instance = new DerivedClass();
var editor = instance.CreateEditor();
What could the compiler infer the type of editor to be? It only knows about BaseClass, so it would have to return an IInterface<BaseClass>. Depending on whether or not T in IInterface<T> is covariant, that could be valid for something which actually implemented IInterface<DerivedClass> or it might not be.
You might want to make your base class generic instead:
public abstract class BaseClass<T> where T : BaseClass<T>
{
public abstract IInterface<T> CreateEditor();
}
public class DerivedClass : BaseClass<DerivedClass>
{
...
}
There are two problems with this:
It doesn't work when you go more than one level deep; if you need subclasses of DerivedClass, you'll have issues
It doesn't prevent malicious abuse of the pattern, e.g. public class NastyClass : BaseClass<DerivedClass>
... but in many cases it's quite practical.
I have an interface with a single generic type parameter:
public interface IDriveable<T> where T : ITransmission { ... }
I also have a class that has a type parameter that needs to be of that interface type:
public class VehicleFactory<T> where T : /* ??? */
There is a problem here with this declaration. I can't put "IDriveable", because that has no type parameters and doesn't match the type signature of IDriveable. But I also don't want to put IDriveable<U> either, because then VehicleFactory has to know what kind of IDriveable it's getting. I want VehicleFactory to accept any kind of IDriveable.
The proposed solution a coworker had was to use:
public class VehicleFactory<T, U> where T : IDriveable<U>
But I don't like this, since it's redundant. I have to say the "U" type twice:
var factory = new VehicleFactory<IDriveable<AllWheelDrive>, AllWheelDrive>();
What should go in the question marks?
What is VehicleFactory going to do with T? Does it actually need the constraint in order to work, or is it just for the sake of developer sanity checking?
One common way round this is to declare a non-generic interface (IDriveable) and then make your generic one extend that:
public interface IDriveable {}
public interface IDriveable<T> : IDriveable {}
public class VehicleFactory<T> where T : IDriveable
If you do want the factory to be able to do things with T, you could put any interface members from IDriveable<T> which don't care about T into the nongeneric IDriveable.
Does this work for you?
public class VehicleFactory<T, U> where T : IDriveable<U>
This will let the factory know what types the driveables are.
You can define the VehicleFactory with 2 types generics and anign one of them to the interface. something like:
public class VehicleFactory<T1,T2> where T1 : IDriveabel<T2>
I hope that ist not what you ment with the Idriveable. I guess the U is a specific type. Like String, etc.
You can shorten often used cases like this:
interface IDriveableAllWheel : IDriveable<AllWheelDrive>
{}
var factory = new VehicleFactory<IDriveableAllWheel, AllWheelDrive>();
or even
class AllWheelFactory : VehicleFactory<IDriveableAllWheel, AllWheelDrive>
{}
also see a kvb's answer in Calling Generic Property In Generic Class From Interface Implemented By Generic Class for possible workaround.