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
Related
I got two bases classes A and B; B is related to A by composition. Also, both classes have constraints on type parameters (generic classes), and in this case class A is constraining one of its parameter type to B's type. So my question is... when constraining class A parameter's type to B's type, why I should specify the parameter's types for class B as well? I only see that I must provide B parameter types when I would inherit the class B, not when its type it's being used for parameter type constraints in a class.
Check a code snippet that illustrates what I'm explaining above:
public abstract class B<TParameter, TResponse>
where TParameter: FooParameter
where TResponse: FooResponse
{
}
public abstract class A<T, TB>
where T: class
where TB: B /*compilation error because B types must be provided... I must define parameter types for class B*/
{
public TB B {get; set;}
public A()
{
//instantiate B property
}
}
public class FooParameter {}
public class FooResponse {}
When you create a generic type, you're creating a conceptual "template" more than a complete type. It can only become a complete type when you specify the missing pieces in the template: the concrete types.
Type constraints are there so the compiler can add some type-checking. Even if you don't know the exact type that's going to be used, if it inherits from a base, you know what base members are available to your code. If you try to constrain a type parameter with a generic type without type arguments, the compiler doesn't have enough information to understand what methods will actually be available.
As an example, if we replace TB: B with the type TB: List<T>, the compiler would not know what the list is of. Methods like Add(T) and Remove(T) couldn't be compiled against, because T would be unknown.
For type constraints to be valuable, they must constrain the type system in such a way as to add useful information. The only way for that information to be useful is for the type to be concretely specified.
Try:
public abstract class A<T, TB, C, D>
where T : class
where C : FooParameter
where D : FooResponse
where TB : B<C,D> {
public TB B { get; set; }
public A()
{
//instantiate B property
}
}
You need to be able to define all of the types that a generic needs at compile time, which you can as above.
I have the generic types A<T> and B<T> and now I want to construct the type C<T> : A<B<T>> which as you can see is a very specific kind of A<T>.
It tried defining C just like that but I get
The type `A<B<T>>' does not contain a constructor that takes `0' arguments
Just in case I built the constructor
public C () {}
but I still get the error.
Note: This is an abstraction of the problem. Assume that A and B have a constructor with the form
public A/B (T t)
Your generic type declaration is okay, but your parameterless constructor doesn't have a counterpart in A<T>.
You should call the base class' constructor with the parameters it requires.
Something like:
public C() : base("param1", "param2", 3)
{
}
In Java, it is possible to bind the type parameter of a generic type. It can be done like this:
class A<T extends B> {
...
}
So, the type parameter for this generic class of A should be B or a subclass of B.
I wonder if C# has a similar feature. I appreciate if somebody let me know.
Thanks
The same in C# is:
class A<T> where T : B
{
}
Also see "Constraints on Type Parameters" (msdn) for a great overview of constraints in general.
Very similar:
public class A<T> where T : B
{
// ...
}
This can be used to constrain T to be a sub-class or implementation of B (if B is an interface).
In addition, you can constrain T to be reference type, value type, or to require a default constructor:
where T : class // T must be a reference type
where T : struct // T must be a value type
where T : new() // T must have a default constructor
Of course you can:
class A<T> where T: B
{
// ...
}
Yes, you can do this, it's called type constraints. Here is an article that explains how:
http://msdn.microsoft.com/en-us/library/d5x73970.aspx
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.
}
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.