Reputation: 563
public interface IRule <T>
{
T Execute();
}
public class Rule : IRule<int>
{
public int Execute()
{
return 10;
}
}
IRule<int> rule = new Rule();
var genericRule = (IRule<object>) rule;
//Exception has occurred: CLR/System.InvalidCastException An exception of ype 'System.InvalidCastException' occurred in test.dll but was not handled in user code: 'Unable to cast object of type 'test.GenericCasting.Rule' to type test.GenericCasting.IRule`1[System.Object]'.'
In business logic, I need to load all Rules objects through reflection and don't have the knowledge of the type used in the Rule object. Any idea how to solve this?
Upvotes: 0
Views: 473
Reputation: 18013
IRule<object>
is not a base type of IRule<int>
so the cast does not work.
Starting with .NET 4 interfaces can be marked covariant but that can be used only in special cases: the generic argument must be used in output position only (return value) and only reference types can be cast to each other. In your case the first condition is met but the second one is not.
A possible solution for this case is to introduce a nongeneric base type. This is how a List<T>
of an unknown type argument can be used as an IList
as well, for example.
public interface IRule
{
object Execute();
}
public interface IRule<T> : IRule
{
new T Execute();
}
Implementation:
public class Rule : IRule<int>
{
// this will be the public implementation (preferred way)
public int Execute() => 42;
// the nongeneric implementation if the object is cast to IRule
object IRule.Execute() => Execute();
}
And now this will work:
IRule<int> rule = new Rule();
// ...
IRule anyRule = rule;
Upvotes: 2
Reputation: 8498
In order to perform typecast in your example, there are two conditions that have to be met:
IRule<T>
must be co-variant, that is, IRule<out T>
T
, that is, you cannot use int
for T
, but you can use string
for example.A working example:
public interface IRule <out T>
{
T Execute();
}
public class Rule : IRule<string> // T must be a reference type
{
public string Execute()
{
return "10";
}
}
//....
IRule<string> rule = new Rule();
var genericRule = (IRule<object>) rule;
As @Servy correctly mentioned, I explained why the original code won't work, but I didn't explain how the original problem can be solved.
Here is how:
// define a separate interface for non-typed invocation
public interface IRule
{
object ExecuteGeneric();
}
public interface IRule<T> : IRule
{
T Execute();
}
// every rule implements both typed and non-typed invocation interface
public class Rule : IRule<int>
{
public int Execute()
{
return 10;
}
object IRule.ExecuteGeneric()
{
return Execute();
}
}
//.....
IRule<int> rule = new Rule();
IRule genericRule = rule;
// perform non-typed invocation on rules of any T
var genericResult = genericRule.ExecuteGeneric();
Upvotes: 2