C#中的泛型 - 无法将“类名”转换为“通用类”

时间:2012-02-29 15:01:18

标签: c# generics

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

我收到以下错误:

Cannot convert type 'Amber.BLL.iWeb.Session.AppSession' to 'TService'   

以下是代码:

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

正如它所发生的那样,AppSession类实现了IAppSession接口。如果我更改实例化AppSession的代码行以使用该接口,如下所示:

IAppSession session = new AppSession();

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

AppSession session = new AppSession();
return (TService) (IAppSession) session;

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

public virtual TService GetService<TService>() where TService : class

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

我注意到this question正在询问类似的问题,但答案不够详细,无法让我了解它如何适用于我的情况。

3 个答案:

答案 0 :(得分: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
    }    
}

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

  

为什么演员直接从TD非法?

假设它是合法的。那么E.M<D>(new D())就可以了。我们将T转换为D,实际上它是D,所以没问题。

现在假设我们用:

创建一个完全不同的程序集
class C 
{
    public static explicit operator D(C c) { whatever }
}

你在那个集会中打电话给E.M<C>(new C()) ..你有理由期待发生什么?你有一个C类型的对象,它被转换为D,并且有一个显式转换运算符就在那里C到{{1} }。大多数人都会合理地期望调用显式转换运算符。

但是编译器在编译D 的主体时应该如何实现,以后某人可能会完全创建一个类M不同的集会?编译C时,编译器无法发出对转换运算符的调用。所以我们有三个选择:

  1. 制作演员有时会使用显式转换运算符,有时则不会,这取决于你是否使用泛型。
  2. 使转换运算符在运行时再次启动编译器,以查找在编译原始代码后可能已在不同程序集中添加的显式转换运算符。
  3. 首先禁止演员。
  4. 简而言之,我们的选择是(1)使泛型不一致,(2)使泛型变得缓慢且不可预测,或(3)禁止已经反对泛型的功能。这是一个很容易做出的选择;我们选择了(3)。

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

      

    为什么通过对象合法地从dynamicT间接投射?

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

      

    为什么通过DTD的间接投票是合法的?

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

    奖金问题:

      

    但是I甚至没有实现D!怎么了?

    I的派生类可能:

    D

    现在class F : D, I {} ... E.M<D>(new F()); 可以投放到t,因为可能实施I,而I可以投放到I因为可能D

    如果FD,那么从sealed投射到I是不合法的,因为那时可能不会有派生的D类型

答案 1 :(得分:2)

您可以使用(TService)(Object)instance转换对象,或在泛型参数上使用约束。

编译器对演员表示满意的原因很简单,他并不知道接口实例背后的内容,因此演员可能会成功。当从类转换为泛型类型T时,他不知道转换是否有效并在编译时抛出错误。当然,他也可以在编译后做到这一点,但有充分的理由,想象......

return (TService)myinstance;

交换:

return (CustomService)myinstance;

myinstance变量与CustomService类型不兼容。

但是从TService接口的演员表可能会成功,或者从ObjectTService

答案 2 :(得分:2)

您是否尝试为IAppSession添加约束?

public virtual TService GetService<TService>() where TService : IAppSession, class

linked question完全是同一个问题。编译器不知道TService可以是AppSession