I have a situation where I’d like the behaviour of the compiler explained. Given a little code:
interface IFoo<T>
{
T Get();
}
class FooGetter : IFoo<int>
{
public int Get()
{
return 42;
}
}
The following compiles and runs:
static class FooGetterGetter
{
public static IFoo<T> Get<T>()
{
return (IFoo<T>)new FooGetter();
}
}
If we make a change to the signature of the Foo class and add the sealed keyword:
sealed class FooGetter : IFoo<int> // etc
Then I get a compiler error on the following line:
return (IFoo<T>)new FooGetter();
Of:
Cannot convert type ‘MyNamespace.FooGetter’ to ‘MyNamespace.IFoo<T>’
Can someone explain what is happening here with regards to the sealed keyword? This is C# 4 against a .NET 4 project in Visual Studio 2010.
Update: interestingly enough I stumbled on that part of the behaviour when I was wondering why the following code fixes it when sealed is applied:
return (IFoo<T>)(IFoo<int>)new FooGetter();
Update: just for clarification, it all runs fine when the type of T requested is the same as the type of T used by the concrete type. If the types differ, the cast fails at runtime with something like:
Unable to cast object of type ‘MyNamespace.StringFoo’ to type
‘MyNamespace.IFoo`1[System.Int32]’
In the above example, StringFoo : IFoo<string> and the caller asks to get an int.
Because
FooGetteris an explicit implementation ofIFoo<int>instead of implementingIFoo<T>generically. Since it is sealed, the compiler knows there’s no way to cast it to a genericIFoo<T>ifTis anything other than anint. If it were not sealed, the compiler would allow it to compile and throw an exception at runtime ifTwas not anint.If you try to use it with anything other than an
int(e.g.FooGetterGetter.Get<double>();) you get an exception:What I’m not sure of is why the compiler does not generate an error for the non-sealed version. How could your sub-class
FooGettersuch thatnew FooGetter()give you anything that implementsIFoo<{something_other_than_int}>?Update:
Per Dan Bryant and Andras Zoltan there are methods to return a derived class from a constructor (or possibly more precisely for the compiler to return a different type by analyzing attributes). So technically this is feasible if the class is not sealed.