Unexpected Generics Behavior - c#

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.

Related

Can't cast derived type to base abstract class with type parameter

I have a simple factory method which provides a concrete implementation instance based on a generic type parameter provided. If the concrete classes inherit from a common abstract base class with a type parameter I cannot cast them. The compiler tells me Error 2 Cannot convert type 'Car' to 'VehicleBase<T>'. It works fine if I substitute the abstract class for an interface with the same type parameter, or if I remove the generic type parameter from the abstract class.
interface IWheel
{
}
class CarWheel : IWheel
{
}
abstract class VehicleBase<T>
{
}
class Car : VehicleBase<CarWheel>
{
}
class VehicleFactory
{
public static VehicleBase<T> GetNew<T>()
{
if (typeof(T) == typeof(CarWheel))
{
return (VehicleBase<T>)new Car();
}
else
{
throw new NotSupportedException();
}
}
}
This fails to compile on (VehicleBase<T>)new Car(). Is this a compiler defect, or could this be a deliberate design decision to treat abstract classes and interfaces with type parameters differently?
As a workaround I can always make the abstract class implement an interface and use this as the return value for my factory method, but I'd still like to know why this behavior is happening.
That is not provable, because generic code needs to work (with the same IL) for every possible T, and there is nothing to say that Car : VehicleBase<float>, for example. The compiler does not over-analyse the fact that the if check sows that T is CarWheel - the static checker treats each statement separately, it doesn't try to understand the cause-and-effect of conditions.
To force it, cast to object in the middle:
return (VehicleBase<T>)(object)new Car();
However! Your approach isn't really "generic" as such.
This is neither a compiler defect nor a deliberate decision. Type parameters on generic classes are neither covariant nor contravariant, ie there is no inheritance relationship between specializations of the same generic class. From the docs:
In the .NET Framework version 4, variant type parameters are restricted to generic interface and generic delegate types.
Which means that the following code will compile, because it uses an interface instead of an abstract class:
interface IWheel
{
}
class CarWheel : IWheel
{
}
interface IVehicleBase<T>
{
}
class Car : IVehicleBase<CarWheel>
{
}
class VehicleFactory
{
public static IVehicleBase<T> GetNew<T>()
{
if (typeof(T) == typeof(CarWheel))
{
return (IVehicleBase<T>)new Car();
}
else
{
throw new NotSupportedException();
}
}
}
Check "Covariance and Contravariance in Generics" for more info and examples.
There is also a Covariance and Contravariance FAQ at the C# FAQ blog with more info, and an 11-part series! on the subject by Eric Lippert
This seems to work:
return new Car() as VehicleBase<T>;
My guess why it is that way:
As generic type instances of VehicleBase<T> are not related, it cannot be proven that casting them could work:
If T is of type Blah, the cast would not work. You can't go back to object and then take the other branch (there's no multiple inheritance in C# after all).
By casting it back to object before, you are again opening the possibility that the cast might work, because there might still be a path down to VehicleBase<CarWheel>. An interface, of course, can appear anywhere in this tree below object, so that should work, too.

What's the point of having constraints for type parameters in .NET (base class and interface constraints)

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.

Can I use the explicit operator to create a derived class?

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.

Casting generics and the generic type

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.

C# Generics methods and returning a object of a parameterized type created in the method from xml

I have a method where I would like to return an object instance of parameterized type T ie. Foo<T>.
The type T is instantiated within the method using GetType(), from a string element in an XML file. Since neither the class or method knows about it before it is created, I cant parameterize either.
Is there a way I can return an object of type Foo<T> from the non-generic method?
EDIT: That is a method signature such as:
public Foo<T> getFooFromXml(string name) {
where the type is created inside, and the method and class are both non-generic?
Yeah, basically you have to get the open generic type and create a closed generic type.
Type openType = typeof(Foo<>);
Type closedType = openType.MakeGenericType(typeof(string));
return Activator.CreateInstance(closedType); // returns a new Foo<string>
EDIT: Note that I used typeof(Foo<>) above, I intentionally left the angle brackets empty.
In response to your edit:
That method signature isn't valid anyway. You need to know T at compile time in order to return Foo from a method. Consider my suggestion in the comments on my last answer where you would have a separate interface IFoo that Foo implements.
class Foo<T> : IFoo {
public T DoSomething() {
...
}
object IFoo.DoSomething() {
return DoSomething();
}
}
interface IFoo {
object DoSomething();
}

Categories

Resources