更新:这不是要让它编译。问题是,为什么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正在询问类似的问题,但答案不够详细,无法让我了解它如何适用于我的情况。
答案 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
}
}
让我们把你的问题分成三个问题。
为什么演员直接从
T
到D
非法?
假设它是合法的。那么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)禁止已经反对泛型的功能。这是一个很容易做出的选择;我们选择了(3)。
如果你想要(2),你可以用C#4; M
在运行时再次启动编译器,并确定是否存在显式转换运算符。
为什么通过对象合法地从
dynamic
到T
间接投射?
因为现在没有用户定义的转换可能是相关的;从来没有用户定义的从对象到任何东西的转换。
为什么通过
D
从T
到D
的间接投票是合法的?
因为现在没有用户定义的转换可能是相关的;从来没有用户定义的从接口到任何东西的转换。
奖金问题:
但是
I
甚至没有实现D
!怎么了?
I
的派生类可能:
D
现在class F : D, I {}
...
E.M<D>(new F());
可以投放到t
,因为可能实施I
,而I
可以投放到I
因为可能是D
。
如果F
为D
,那么从sealed
投射到I
是不合法的,因为那时可能不会有派生的D
类型
答案 1 :(得分:2)
您可以使用(TService)(Object)instance
转换对象,或在泛型参数上使用约束。
编译器对演员表示满意的原因很简单,他并不知道接口实例背后的内容,因此演员可能会成功。当从类转换为泛型类型T时,他不知道转换是否有效并在编译时抛出错误。当然,他也可以在编译后做到这一点,但有充分的理由,想象......
return (TService)myinstance;
交换:
return (CustomService)myinstance;
且myinstance
变量与CustomService
类型不兼容。
但是从TService
接口的演员表可能会成功,或者从Object
到TService
。
答案 2 :(得分:2)
您是否尝试为IAppSession
添加约束?
public virtual TService GetService<TService>() where TService : IAppSession, class
那linked question完全是同一个问题。编译器不知道TService
可以是AppSession
。