Reputation: 3141
I have the following:
public interface IInput
{
}
public interface IOutput
{
}
public interface IProvider<Tin, Tout>
where Tin : IInput
where Tout : IOutput
{
}
public class Input : IInput
{
}
public class Output : IOutput
{
}
public class Provider : IProvider<Input, Output>
{
}
I would like to be able to get the following method to cast the result:
private static IProvider<IInput, IOutput> GetProvider()
{
return new Provider();
}
I know the types are different but they implement the interfaces.
Any clue?
Upvotes: 0
Views: 268
Reputation: 64923
I'm going to take the risk of explaining you an alternate approach.
Maybe I'm mistaken. It seems like you're looking to create provider instances using a factory method that will be somewhere else.
If you go this way, you can't avoid an explicit cast here:
private static IProvider<IInput, IOutput> GetProvider()
{
// Explicit upcast
return (IProvider<IInput, IOuput>)new Provider();
}
Some suggestion: common naming scheme for generic parameters is a capital T
and a pascal-cased identifier like TOutput
.
You need this cast because C# compiler knows that TOutput
must implement IOutput
, and TInput
must implement IInput
thanks to generic constraints, and even when Provider
has given generic arguments that full-fills the whole constraints, the problem comes when the factory method can't implicitly prove that generic parameters TOutput
/TInput
for Provider
class are the same as ones given to the factory method itself:
public static IProvider<TInput, TOutput> GetProvider<TInput, TOutput>()
where TInput : IInput
where TOutput : IOutput
{
// Hey!!!!!!!! Do TOutput and TInput of this method are the same as
// Provider TOutput and TInput? Who knows, thus, compiler error:
// Cannot implicitly convert type 'Provider' to 'IProvider<TInput,TOutput>'.
// An explicit conversion exists (are you missing a cast?)
return new Provider();
}
Check this working sample in DotNetFiddle.
Upvotes: 1
Reputation: 127563
The only way to get this to work is if IProvider
is covariant in its generic properties.
public interface IProvider<out Tin, out Tout>
where Tin : IInput
where Tout : IOutput
{
}
Doing that gets rid of your compiler error for your example code, but because you did not show what you are doing with Tin
and Tout
inside IProvider
I can't say if it will give you other compiler errors or not.
The reason you must declare it covariant to be able to use it as a parameter is you could potentially bad things if it was not. Here is a simplified example to show how it could go wrong:
public interface IAnimal { }
public class Dog : IAnimal { }
public class Cat : IAnimal { }
public interface IAnimalCollection<TAnimal> where TAnimal : IAnimal
{
TAnimal GetAnimal(int i);
int AddAnimal(TAnimal animal);
}
public class DogCollection : IAnimalCollection<Dog>
{
private List<Dog> _dogs = new List<Dog>();
public Dog GetAnimal(int i)
{
return _dogs[i];
}
public int AddAnimal(Dog animal)
{
_dogs.Add(animal);
return _dogs.Count - 1;
}
}
class Program
{
static void Main(string[] args)
{
IAnimalCollection<IAnimal> animalCollection = GetDogCollection();
animalCollection.AddAnimal(new Cat()); //We just added a cat to a collection of dogs.
}
private static IAnimalCollection<IAnimal> GetDogCollection()
{
return new DogCollection();
}
}
If we where allowed to return a DogCollection
as a IAnimalCollection<IAnimal>
that makes adding a Cat
perfectly legal due to it being a IAnimal
and AddAnimal
would be int AddAnimal(IAnimal)
.
By adding out
this makes the AddAnimal
method illegal, but now it is perfectly safe to say "This collection of dogs is represented as a collection of animals" because now there is no way to add to the collection an animal that is not a dog.
To make the above code compile, you must make one of two tradeoffs. Either make GetDogCollection()
not return a interface and delete the addition of the cat.
class Program
{
static void Main(string[] args)
{
IAnimalCollection<IAnimal> animalCollection = GetDogCollection();
//animalCollection.AddAnimal(new Cat()); //Would get a compiler error if we tried to add a cat to the collection
}
private static IAnimalCollection<Dog> GetDogCollection()
{
return new DogCollection();
}
}
Or make the interface covariant, but to do that you would need to delete the AddAnimal
method from the interface, which also would not let you add a cat to the collection.
public interface IAnimalCollection<out TAnimal> where TAnimal : IAnimal
{
TAnimal GetAnimal(int i);
//int AddAnimal(TAnimal animal); //Can't have methods that take in the type when using "out"
}
class Program
{
static void Main(string[] args)
{
IAnimalCollection<IAnimal> animalCollection = GetDogCollection();
//animalCollection.AddAnimal(new Cat()); //Would get a compiler error because this method no longer exists.
}
private static IAnimalCollection<IAnimal> GetDogCollection()
{
return new DogCollection();
}
}
Upvotes: 1