class Base
{
}
class Derived1 : Base
{
}
class Derived2 : Base
{
public static explicit operator Derived1(Derived2 d2)
{
return new Derived1();
}
}
class Test
{
static void Main()
{
Base bd2 = new Derived2();
Derived1 d2ConvertedD1 = (Derived1)bd2; //throws InvalidCastException
}
}
Unable to cast object of type 'ConsoleApplication1.Derived2' to type 'ConsoleApplication1.Derived1'.
Why? What is wrong with my operator conversion?
The trouble is that your custom conversion isn't being used, because the compile-time type of bd2 is Base, not Derived2. The compiler isn't even considering your custom conversion, so it's just including a normal cast - which is failing for the obvious reason. (I assume you understand that failure, otherwise you wouldn't have created the custom conversion to start with.)
Operators and conversions are chosen by the compiler based on the compile-time types of the operands.
While you could cast to Derived2 first or change the declaration of bd2, I would personally change tack completely, and look at the bigger picture again. What are you trying to do, and why? Would a virtual method in Base make more sense, for example?
You can only cast classes up and down the hierarchy, not across.
Look at the signature of your operator:
public static explicit operator Derived1(Derived2 d2);
Notice it's static. What you're seeing is similar to the limitation of method overload resolution.
It's essentially the same reason the below outputs "Object" instead of "String":
static void WriteObject(object obj) { Console.WriteLine("Object"); }
static void WriteObject(string str) { Console.WriteLine("String"); }
object obj = "I am a string.";
WriteObject(obj);
That is, the compiler needs to pick an overload at compile-time. In the case of casting from a Base to a Derived1, there is no overload with the proper signature, so it attempts an actual downcast. Declaring bd2 as Derived2, as others have mentioned, would "fix" this by enabling the compiler to select your custom conversion.
The class Derived2 does not inherit from the class Derived1, that is why this fails.
The following would be valid:
Base d2ConvertedBase = (Base) bd2;
If Derived2 was to inherit from Derived1, what you tried would be valid.
Cast to the base class. If you're creating a complex class heirarchy, consider implementing interfaces and doing interface based programming.
Related
I have written the following piece of code in an attempt to provide a type-safe interface:
namespace MWE
{
public abstract class C {}
public class A : C {}
public class B : C {}
public class Container<T> where T : C
{
public readonly T Value;
public static implicit operator T(Container<C> c)
{
return c.Value;
}
}
public interface IWrapper<out TC> where TC : C {}
public class Foo
{
public Foo(IWrapper<Container<C>> wrapper) {}
}
}
Unfortunately this doesn't compile. The Compiler<C>-part of the wrapper parameter to the Foo constructor causes the compiler to produce the following error:
The type 'MFE.Container<MFE.C>' cannot be used as type parameter 'TC' in the generic type or method 'IWrapper<TC>'. There is no implicit reference conversion from 'MFE.Container<MFE.C>' to 'MFE.C'.
The type 'MFE.Container<MFE.C>' must be convertible to 'WeirdTestStuff.C' in order to use it as parameter 'TC' in the generic interface 'MFE.IWrapper<out TC>'.
I can't figure out where the problem is exactly, since the Covariance for the conversion seems to be there and there is even an implicit conversion from a Container<T> to T defined. Since T : C, I assumed it should just work like this.
I'd like to keep Foo's constructor as is if possible.
I hope someone can point me to a solution of this problem
even an implicit conversion from a Container to T defined
That is true but that's not what the compiler requires. It requires:
implicit reference conversion
(My emphasis)
An implicit reference conversion is not one supplied by any user-defined operator and is allowed only when one type derives (directly or via intermediate types) from the other1.
Container has-a C and can be converted to a C via a user-defined operator but that's not enough to make it be-a C. Your question is too abstracted to say what the fix should be here - should Container be non-generic and derived from C? That's the obvious way to "shut up" the compiler but may not solve your actual problem.
You can't use generics to make a type's base-type settable at runtime.
1These are Eric Lippert's Representation-preserving conversions
I'm working on a small class library at work, and it naturally involves using generics for this task. But there is this thing that I don't really understand with generics:
Why would I need to use generic type parameters, and then constrain the the type parameter to a specific base class or interface.
Here's an example to what I mean:
public class MyGenericClass<T> where T : SomeBaseClass
{
private T data;
}
And here's the implementation without generics
public class MyClass
{
private SomeBaseClass data;
}
Are these two definitions the same (if yes, then i don't see the advatage of using generics here)?
If not, what do we benefit from using generics here?
As with almost all uses of generics, the benefit comes to the consumer. Constraining the type gives you the same advantages that you get by strongly typing your parameter (or you can do other things like ensure that there's a public parameterless constructor or ensure that it's either a value or reference type) while still retaining the niceties of generics for the consumer of your class or function.
Using generics also, for example, allows you to obtain the actual type that was specified, if that's of any particular value.
This example is a little contrived, but look at this:
public class BaseClass
{
public void FunctionYouNeed();
}
public class Derived : BaseClass
{
public void OtherFunction();
}
public class MyGenericClass<T> where T: BaseClass
{
public MyGenericClass(T wrappedValue)
{
WrappedValue = wrappedValue;
}
public T WrappedValue { get; set; }
public void Foo()
{
WrappedValue.FunctionYouNeed();
}
}
...
var MyGenericClass bar = new MyGenericClass<Derived>(new Derived());
bar.Foo();
bar.WrappedValue.OtherFunction();
The difference is that the former defines the new class as a specific type; the latter simply defines a plain class with a field of that type.
It's all about type safety. Using generics you can return a concrete type (T) instead of some base type which defines the API you need in your generic class. Therefore, the caller of your method won't have to cast the result to the concrete type (which is an error-prone operation).
The main difference is in usage. In the first case, the usage can have:
MyGenericClass<SomeDerivedClass> Variable
Variable.data.SomeDerivedProperty = X
And so that when you use that class, you can still access anything from SomeDerivedClass without casting back to it.
The second example will not allow this.
MyClass.data = SomeDerivedClassInstance
MyClass.data.SomeDerivedProperty = X //Compile Error
((SomeDerivedClass)MyClass.data).SomeDerivedProperty = X //Ewwwww
You will have to cast back up to the SomeDerivedClass (which is unsafe) to use something specific to the derived class.
I don't think that there is a huge amount of difference except that the generic version is constraining your Class, whereas the second is just a constraint on a member of the class. If you added more members and methods to your first Class, you would have the same constraint in place.
I was discussing a related matter with someone on chat and I came up with this code which behaved differently than I had expected.
class Program
{
static void Main(string[] args)
{
new Test<SomeObject>();
Console.ReadKey();
}
}
class SomeObject
{
public SomeObject() { }
public new string ToString()
{
return "Hello world.";
}
}
class Test<T> where T : new()
{
public Test()
{
T t = new T();
object t1 = t;
Console.WriteLine(t.ToString());
Console.WriteLine(t1.ToString());
}
}
The output is:
<ProjectName>.SomeObject
<ProjectName>.SomeObject
Because the first line is written from the generic type I expected it to use the ToString() method defined in SomeObject since that's what the type would become at run time wouldn't it?
I believe that Ben Voigt has given you the answer in his comment.
You could achieve the result you’re expecting by specifying the type that declares the hiding (new) method implementation as a generic constraint:
class Test<T> where T : SomeObject, new()
{
public Test()
{
T t = new T();
object t1 = t;
Console.WriteLine(t.ToString());
Console.WriteLine(t1.ToString());
}
}
This outputs:
Hello world.
Program.SomeObject
Edit: The compiler resolves any member invocations on generic types against the generic constraints. This is implied in the MSDN C# Programming Guide on Constraints on Type Parameters:
By constraining the type parameter, you increase the number of allowable operations and method calls to those supported by the constraining type and all types in its inheritance hierarchy. Therefore, when you design generic classes or methods, if you will be performing any operation on the generic members beyond simple assignment or calling any methods not supported by System.Object, you will have to apply constraints to the type parameter.
To help clarify matters: Imagine that you had defined a new method, Foo, in your class:
class SomeObject
{
public SomeObject() { }
public void Foo() { }
}
Attempting to call Foo would result in a compile-time error. The only thing the compiler knows about generic type T is that it has a parameterless constructor – it has no knowledge of any methods it might define.
class Test<T> where T : new()
{
public Test()
{
T t = new T();
t.Foo(); // Error: 'T' does not contain a definition for 'Foo'
// and no extension method 'Foo' accepting a
// first argument of type 'T' could be found
}
}
However, if you constrain T to be of type SomeObject, then the compiler would know to look for the definition of Foo within the SomeObject class:
class Test<T> where T : SomeObject, new()
{
public Test()
{
T t = new T();
t.Foo(); // SomeObject.Foo gets called
}
}
The reasoning is quite similar for hidden members.
In Test<T>, the compiler doesn't know that T will actually be SomeObject, since there's no constraint on T. So it can only assumes that t is an object, and the call to t.ToString() results in calling the virtual Object.ToString method, not SomeObject.ToString()
This has absolutely nothing to do with generics, and everything to do with the fact that you declared a new method called ToString, instead of overriding the existing one.
If you had overridden the method instead, it would've been used, this has nothing to do with early-bound compared to late-bound either (as indicated by other answers here.)
The reason why calling ToString on the T reference does not differ from the object reference is that the compiler has no way to verify that all T's that can be used here has defined that new ToString method, and it thus has to fall back to the one inherited from object in all cases.
Note that the compiler will produce a method that will be used by all variations of T, regardless, so it has to use the knowledge it has about T, and you have not stated that you inherit from an interface or a class (although in this case I doubt it would make a difference) so the compiler has no way of knowing that ToString has been overridden in this case.
If, on the other hand, you had stated that the T in question is a descendant of SomeObject, the compiler would know that it has a new ToString method to use, but this knowledge is not available to the compiler with just the T alone.
This has been bothering me for a while. I'm having a little trouble understanding why explicit casts are needed in the following code:
public static class CastStrangeness
{
public class A
{
}
public class B
: A
{
}
public static void Foo<T>(T item)
where T : A
{
// Works.
A fromTypeParameter = item;
// Does not compile without an explicit cast.
T fromConcreteType = (T)fromTypeParameter;
// Does not compile without an explicit cast.
Foo<T>((T)fromTypeParameter);
}
public static void Bar(A item)
{
// Compiles.
Foo<A>(item);
}
}
It seems to me that T is guaranteed to be an A, so surely the compiler could infer that any instance of A is guaranteed to be assignable to T? Otherwise, I wouldn't be able to pass an A or a B into Foo(). So what am I missing?
PS. I've tried searching for endless permutations of keywords on this, but every result seems to wind up referring to covariance and contravariance WRT generic interfaces :)
Take the case of:
Foo(new B());
Your first assignment is ok:
A fromtypeParameter = item;
Since B : A.
But this assignment is not ok:
T fromConcreteType = fromTypeParameter;
Because you could very well have assigned fromTypeParameter as:
fromTypeParameter = new A();
Which you obviously cannot cast to T (which is B in this instance). T is more specific than A, it could be derived from A. So you can go one way but not the other, without an explicit cast (which may fail).
It seems to me that T is guaranteed to be an A, so surely the compiler could infer that any instance of A is guaranteed to be assignable to T?
Well, no.
string is an object. But
string s = new object();
is illegal.
All T's might be A... but not all A's are T.
When you create a type that derives from A and pass it into the generic method, the compiler knows it as T, the derived type. If you do not cast it back it does not know that you want A or T, or any type in the inheritance tree between T and A.
This logic applies whether you are using generics or not.
public class A
{}
public class B : A
{}
public class C: B
{}
A animal = new C();
C cat = animal; // wont compile as it does not know that A is a cat,
// you have to cast even though it looks like
// a cat from the new C();
Even though you have the generic constraint, this tend to apply to the way the generic method is used and prevents the constraint to be broken. It does not apply to any referencing of a derived type through a base reference. Although the compiler 'could' figure it out it may not know that is what your intent was, so it is better for it to be safe and not infer it.
It is less subtle if you use more specific type and variable names:
public class Animal
{}
public class Giraffe : Animal
{}
public static void Foo<TAnimal>(TAnimal animal) where TAnimal : Animal
{
Animal generalAnimal = animal;
TAnimal possiblyMoreSpecificAnimal = (TAnimal) generalAnimal;
// The above line only works with a cast because the compiler doesn't know
// based solely on the variable's type that generalAnimal is a more
// specific animal.
}
Consider, I have the following 3 classes / interfaces:
class MyClass<T> { }
interface IMyInterface { }
class Derived : IMyInterface { }
And I want to be able to cast a MyClass<Derived> into a MyClass<IMyInterface> or visa-versa:
MyClass<Derived> a = new MyClass<Derived>();
MyClass<IMyInterface> b = (MyClass<IMyInterface>)a;
But I get compiler errors if I try:
Cannot convert type 'MyClass<Derived>' to 'MyClass<IMyInterface>'
I'm sure there is a very good reason why I cant do this, but I can't think of one.
As for why I want to do this - The scenario I'm imagining is one whereby you ideally want to work with an instance of MyClass<Derived> in order to avoid lots of nasty casts, however you need to pass your instance to an interface that accepts MyClass<IMyInterface>.
So my question is twofold:
Why can I not cast between these two types?
Is there any way of keeping the niceness of working with an instance of MyClass<Derived> while still being able to cast this into a MyClass<IMyInterface>?
This does not work because C# only supports covariance on the type parameters of interfaces and delegates. If your type parameter exists only in output positions (i.e. you only return instances of it from your class and don't accept it as an argument) you could create an interface like this:
interface IClass<out T> { }
class MyClass<T> : IClass<T> { }
Which would allow you to do this:
IClass<Derived> a = new MyClass<Derived>();
IClass<IMyInterface> b = a;
Honestly that is about as close as you are going to get and this requires the C# 4 compiler to work.
The reason you cannot do this in general is because most classes are not simple empty examples. They have methods:
class MyClass<T>
{
static T _storage;
public void DoSomethingWith(T obj)
{
_storage = obj;
}
}
interface IMyInterface { }
class Derived : IMyInterface { }
MyClass<Derived> a = new MyClass<Derived>();
Now, a has a method DoSomethingWith that accepts a Derived and stores it in a static variable of type Derived.
MyClass<IMyInterface> b = (MyClass<IMyInterface>)a;
If that was allowed, b would now appear to have a method DoSomethingWith that accepts anything that implements IMyInterface, and would then internally attempt to store it in a static variable of type Derived, because it's still really the same object referred to by a.
So now you'd have a variable of type Derived storing... who knows what.