Reputation: 1803
Can someone explain to me why this is incorrect in C#:
namespace NamespaceA
{
public class ClassA<T1>
{
T1 mT1;
public T1 Type1
{
get { return mT1; }
}
}
public class IOForClassA
{
public interface ICanOutput
{
void OutputFunction();
}
public static void Output(ClassA<ICanOutput> aClassA_WithOutputCapabilities)
{
aClassA_WithOutputCapabilities.Type1.OutputFunction();
}
}
}
namespace NamespaceB
{
public class ClassB
{
public class OutputableClassA : NamespaceA.IOForClassA.ICanOutput
{
public void OutputFunction()
{
}
}
public ClassB()
{
NamespaceA.ClassA<OutputableClassA> aOutputableA = new NamespaceA.ClassA<OutputableClassA>();
NamespaceA.IOForClassA.Output(aOutputableA);
}
}
}
This will result in a compile error of:
Argument 1: cannot convert from 'NamespaceA.ClassA"<"NamespaceB.ClassB.OutputableClassA">" to NamespaceA.ClassA"<"NamespaceA.IOForClassA.ICanOutput">"
... but NamespaceB.ClassB.OutputableClassA implements NameSpaceA.IoForClassA.ICanOutput, so I don't see why this is a problem...
I am trying to allow a user to create a ClassA of any type he/she wishes. However, if they wish that ClassA to be "outputable" its templated type must implement a specific interface.
Upvotes: 1
Views: 1322
Reputation: 2596
@syazdani is correct about it being a covariance problem (and he links to a useful article), but his example doesn't then use covariance, which would achieve what you are looking for. Although his answer is correct (and likely preferrable as it has the added benefit of bringing along the actual type in case you need it into the Output
method, I figured I would show you how you could also do this with a covariant interface in case it helps you in any other case where you may use them:
namespace NamespaceA
{
public interface IClassA<out T1>
{
T1 Type1 { get; }
}
public class ClassA<T1> : IClassA<T1>
{
T1 mT1;
public T1 Type1
{
get { return mT1; }
}
}
public class IOForClassA
{
public interface ICanOutput
{
void OutputFunction();
}
public static void Output(IClassA<ICanOutput> aClassA_WithOutputCapabilities)
{
aClassA_WithOutputCapabilities.Type1.OutputFunction();
}
}
}
namespace NamespaceB
{
public class ClassB
{
public class OutputableClassA : NamespaceA.IOForClassA.ICanOutput
{
public void OutputFunction()
{
}
}
public ClassB()
{
NamespaceA.ClassA<OutputableClassA> aOutputableA = new NamespaceA.ClassA<OutputableClassA>();
NamespaceA.IOForClassA.Output(aOutputableA);
}
}
}
Upvotes: 1
Reputation: 7028
OutputableClassA
is derived from ICanOutput
. But it does not mean that ClassA<OutputableClassA>
is also derived from ClassA<ICanOutput>
. Thats why your code is not working.
Link Covariance and Contravariance in Generics might be helpful.
Upvotes: 0
Reputation: 4868
You are hitting up on a covariance/contravariance problem. Basically, only interfaces can allow generic types to be of a more derived type, and only if they are explicitly specified using the in/out keywords (outlined in the linked article).
Reading your code, it seems as if you want to call a known named member of a class without knowing its type, which is something that type constraints might be a little better suited for. You might have to change your design a bit though.
namespace NamespaceA
{
public class ClassA<T1> where T1 : IOForClassA.ICanOutput
{
T1 mT1;
public T1 Type1
{
get { return mT1; }
}
}
public class IOForClassA
{
public interface ICanOutput
{
void OutputFunction();
}
public static void Output<T>(ClassA<T> aClassA_WithOutputCapabilities) where T : IOForClassA.ICanOutput
{
aClassA_WithOutputCapabilities.Type1.OutputFunction();
}
}
}
namespace NamespaceB
{
public class ClassB
{
public class OutputableClassA : NamespaceA.IOForClassA.ICanOutput
{
public void OutputFunction()
{
}
}
public ClassB()
{
NamespaceA.ClassA<OutputableClassA> aOutputableA = new NamespaceA.ClassA<OutputableClassA>();
NamespaceA.IOForClassA.Output(aOutputableA);
}
}
}
Upvotes: 3