EDIT: This is an attempted restatement of Polity s answer - I
think I know what he s trying to say, but I could be wrong.
My original answer (below the line) is still in some ways the
canonical one: the compiler rejects it because the language
specification says it has to :) However, in an attempt to guess
the view of the language designers (I ve never been part of the C#
design committee, and I don t think I ve asked them about this, so
it really is guesswork......) here goes......
We re used to thinking about the validity of conversions "at
compile time" or "at execution time". Usually implicit conversions
are ones which are compile-time-guaranteed to be valid:
string x = "foo";
object y = x;
That can t go wrong, so it s implicit. If something can go wrong,
the language is designed so that you have to tell the compiler,
"Trust me, I believe it ll work at execution time even though you
can t guarantee it now." Obviously there s a check at execution time
anyway, but you re basically telling the compiler you know what
you re doing:
object x = "foo";
string y = (string) x;
Now the compiler already prevents you from attempting conversions
which it believes can never work1 in a useful way:
string x = "foo";
Guid y = (Guid) x;
The compiler knows there s no conversion from string
to Guid
, so
the compiler doesn t believe your protestations that you know what
you re doing: you clearly don t2.
So those are the simple cases of "compile time" vs "execution time"
checking. But what about generics? Consider this method:
public Stream ConvertToStream<T>(T value)
{
return (Stream) value;
}
What does the compiler know? Here we have two things which can vary:
the value (which varies at execution time, of course) and the type
parameter T
, which is specified at a potentially different
compile time. (I m ignoring reflection here, where even T
is only
known at execution time.) We may compile the calling code later,
like this:
ConvertToStream<string>(value);
At that point, the method doesn t make sense if you replace the type
parameter T
with string
, you end up with code which wouldn t
have compiled:
// After type substitution
public Stream ConvertToStream(string value)
{
// Invalid
return (Stream) value;
}
(Generics don t really work by doing this sort of type substitution
and recompiling, which would affect overloading etc - but it can
sometimes be a helpful way of thinking about it.)
The compiler can t report that at the time when the call is
compiled - the call doesn t violate any constraints on T
, and the
body of the method should be viewed as an implementation detail. So
if the compiler wants to prevent the method from ever being called
in a way which introduces a non-sensical conversion, it has to do
so when the method itself is compiled.
Now the compiler/language isn t always consistent in this approach.
For example, consider this change to the generic method, and the
"following type substitution when called with T=string
" version:
// Valid
public Stream ConvertToStream<T>(T value)
{
return value as Stream;
}
// Invalid
public Stream ConvertToStream(string value)
{
return value as Stream;
}
This code does compile in the generic form, even though the
version after type substitution doesn t. So maybe there s a deeper
reason. Maybe in some cases there simply wouldn t be suitable IL to
represent the conversion - and the easier cases aren t worth making
the language more complicated for......
1 It sometimes gets this "wrong", in that there are times
when a conversion is valid in the CLR but not in C#, such as int[]
to uint[]
. I ll ignore these edge cases for the moment.
2 Apologies to those who dislike the anthropomorphisation
of the compiler in this answer. Obviously the compiler doesn t
really have any emotional view of the developer, but I believe it
helps get the point across.
简单的答复是,汇编者抱怨说,语言说明必须如此。 规则载于C# 4光谱第6.2.7节。
特定类型的参数(<代码>)有以下明确的换算: T:
......
- From a type parameter
U
to T
, provided T
depends on U
. (See section 10.1.5.)
这里的<代码>Dogt t依T
,因此不允许转换。
我怀疑这一规则是为了避免一些模糊的玉米案件而制定的,在这种情况下,如果你能够合理地认为它应当是一种有效的转换尝试,那就会造成痛苦,但我怀疑,编纂这一逻辑会使语言更加复杂。
注:替代案文可以是使用as
而不是is
-then-cast:
Dog dog = this._animal as Dog;
if (dog != null)
{
dog.Bark();
}
d 我辩称,单单进行转换,就更清洁。