C#中的泛型 - 无法将'classname'转换为'TGenericClass'

Cha*_*ian 5 c# generics

更新:这不是要让它编译.问题是,为什么C#编译器在使用接口时允许转换,但是当我使用实现相同接口的类时,它无法弄清楚类型.

我收到以下错误:

Cannot convert type 'Amber.BLL.iWeb.Session.AppSession' to 'TService'   
Run Code Online (Sandbox Code Playgroud)

这是代码:

public override TService GetService<TService>() 
{
    if ( typeof( TService ) == typeof( IAppSession ) )
    {
        AppSession session = new AppSession();
        return (TService) session;
    }
    throw new Exception( String.Format( 
        "iWebFactoryProvider cannot create services of type '{0}'.", 
        typeof( TService ).Name ) );
}
Run Code Online (Sandbox Code Playgroud)

实际上,AppSession该类实现了IAppSession接口.如果我更改实例化的代码行AppSession以使用该接口,如下所示:

IAppSession session = new AppSession();
Run Code Online (Sandbox Code Playgroud)

突然间,一切都很好.我还注意到,如果我这样做,它编译得很好:

AppSession session = new AppSession();
return (TService) (IAppSession) session;
Run Code Online (Sandbox Code Playgroud)

如果重要,GetService()将覆盖一个方法,其签名声明如下:

public virtual TService GetService<TService>() where TService : class
Run Code Online (Sandbox Code Playgroud)

简而言之,我无法弄清楚这里的规则是什么,所以我可以知道将来如何避免这种情况.为什么编译器很乐意转换界面,但不乐意投射界面的实现类?

我注意到这个问题是在询问类似的问题,但答案不够详细,我不能理解它是如何适用于我的情况的.

Eri*_*ert 18

为什么C#编译器在使用接口时允许转换,但是当我使用实现相同接口的类时,它无法弄清楚类型?

好问题.考虑以下:

public interface I {}
public class D {} // Note that D does not even implement I!
public class E
{
    public static M<T>(T t)
    {
        D d1 = (D)t; // Illegal
        D d2 = (D)(object)t; // Legal
        D d3 = (D)(I)t; // Legal
    }    
}
Run Code Online (Sandbox Code Playgroud)

让我们把你的问题分成三个问题.

为什么是直接投从TD违法?

假设它是合法的.然后E.M<D>(new D())会工作得很好; 我们就投了TD,事实上它是一个D,所以没有问题.

现在假设我们创建了一个完全不同的程序集:

class C 
{
    public static explicit operator D(C c) { whatever }
}
Run Code Online (Sandbox Code Playgroud)

你召集E.M<C>(new C())那个大会..你有理由期待发生什么?您有类型的对象C,它被强制转换为D,并没有一个明确的转换运营商那里CD.大多数人会合理地期望调用显式转换运算符.

但是,如何在地球上时,编译器应该实现编制的身体的时候M有人在将来可能会创建一个类C在一个完全不同的组件?编译时,编译器无法向转换运算符发出调用M.所以我们有三个选择:

  1. 使转换运算符有时使用显式转换运算符,有时不使用,具体取决于您是否使用泛型.
  2. 使转换运算符在运行时再次启动编译器,以查找在编译原始代码后可能已在不同程序集中添加的显式转换运算符.
  3. 首先禁止演员表演.

简而言之,我们的选择是(1)使泛型不一致,(2)使泛型变得缓慢和不可预测,或(3)禁止已经起作用于反对泛化的特征.这是一个很容易做出的选择; 我们选择了(3).

如果你想要(2),你可以用C#4; dynamic在运行时再次启动编译器并确定是否存在显式转换运算符.

为什么投间接地从TD通过对象是否合法?

因为现在没有用户定义的转换可能是相关的; 从来没有用户定义的从对象到任何东西的转换.

为什么投间接地从TD通过I法律?

因为现在没有用户定义的转换可能是相关的; 从来没有用户定义的从接口到任何东西的转换.

奖金问题:

但是D甚至没有实施I!那是怎么回事?

派生类的D权力:

class F : D, I {}
...
E.M<D>(new F());
Run Code Online (Sandbox Code Playgroud)

现在t可以转换到I,因为它可能会实现I,并且I可以转换为D,因为它可能F.

如果Dsealed那就不合法,从投ID,因为那么不可能是派生F型.

  • @supercat:我们C#团队已经对泛型类型的性能进行了大量的测试,特别是奇怪的情况,例如具有数百个类型参数的泛型,具有深度递归结构的泛型等等.我们没有遇到任何值得注意的问题. (2认同)