通用类型转换异常

时间:2019-06-28 20:20:06

标签: c# generics

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]'.'

在业务逻辑中,我需要通过反射来加载所有Rule对象,并且不了解Rule对象中使用的类型。知道如何解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

要在您的示例中执行类型转换,必须满足两个条件:

  1. IRule<T>必须是协变的,即IRule<out T>
  2. 协方差仅适用于引用类型为T的情况,也就是说,不能将int用于T,但是可以使用string

一个工作示例:

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;

编辑

正如@Servy正确地提到的那样,我解释了为什么原始代码不起作用,但没有解释如何解决原始问题。

方法如下:

// 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();

答案 1 :(得分:2)

T=None不是IRule<object>的基本类型,因此强制类型转换无效。

从.NET 4接口开始可以标记为协变,但只能在特殊情况下使用:泛型参数必须仅在输出位置(返回值)中使用,并且只能将引用类型强制转换为彼此。在您的情况下,第一个条件得到满足,但第二个条件没有满足。

在这种情况下,可能的解决方案是引入非通用基类型。例如,这也可以将未知类型参数的IRule<int>用作List<T>

IList

实施:

public interface IRule 
{
    object Execute();
}

public interface IRule<T> : IRule
{
    new T Execute();
}

现在这将起作用:

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();
}