Why C# Func<int> cannot be assigned to Func<object>? - c#

I've studied Covariant and Contravariant in C#.
and below my code has an error.
object a = new object();
int b = 10;
a = b; // not error
Func<object> acO = () => new object();
Func<int> acI = () => 1;
acO = acI; // error
Error Message:
Cannot implicitly convert type 'System.Func' to type 'System.Func'.
I thought that if int -> object is possible, Func -> Func will be possible.
but it is not.
I think value type will be copied to use when it is returned unlike reference type(object) and it can cause unintentional operation(like exception). Is my guess correct?
I'm looking forward to your wise answers.
Thank you for reading.

From the Variance in Delegates (C#) docs:
Variance for generic type parameters is supported for reference types only.
A nice blog post diving into the topic by Eric Lippert:
All the built-in reference conversions are identity-preserving. This is why covariant and contravariant conversions of interface and delegate types require that all varying type arguments be of reference types. To ensure that a variant reference conversion is always identity-preserving, all of the conversions involving type arguments must also be identity-preserving. The easiest way to ensure that all the non-trivial conversions on type arguments are identity-preserving is to restrict them to be reference conversions.

Related

Why `IEnumerable<int>` is not `IEnumerable<object>`?

IEnumerable<T> interface is covariant, so IEnumerable<string> is IEnumerable<object>.
But why IEnumerable<int> is not IEnumerable<object> while int is object?
Variance conversions like this are only supported if there is an implicit reference or identity conversion between the type parameters. As the spec says:
A type T<Aᵢ, ..., Aᵥ> is variance-convertible to a type T<Bᵢ, ..., Bᵥ> if T is either an interface or a delegate type declared with the variant type parameters T<Xᵢ, ..., Xᵥ>, and for each variant type parameter Xᵢ one of the following holds:
Xᵢ is covariant and an implicit reference or identity conversion exists from Aᵢ to Bᵢ
Xᵢ is contravariant and an implicit reference or identity conversion exists from Bᵢ to Aᵢ
Xᵢ is invariant and an identity conversion exists from Aᵢ to Bᵢ
There is neither a reference not identity conversion between int and object, only a boxing conversion.
You might be wondering what's so special about implicit reference and identity conversions, that they allow variance conversions. Well, these conversions don't require the runtime to do anything at all!
To convert from a string to object, for example, is purely a compile-time matter. Nothing needs to be done to the bits representing the string, in order to turn it into an object. After all, every string is an object.
Because of this, converting from IEnumerable<string> to IEnumerable<object> is trivial too - the runtime does not need to do anything.
This is not true for the int-to-object conversion, however. Because int is a value type and object is a reference type. to maintain these semantics, the runtime needs to create a reference type wrapper around that int in order to do the conversion, "boxing" it.
This means that the runtime cannot just "do nothing" if it wants to convert a IEnumerable<int> to IEnumerable<object>. You would expect to get reference types out of that IEnumerable, not value types, so when and how does the "boxing" happen? When and how should this happen in general, not just in the case of IEnumerable<T>?
I'm sure this in theory can all be figured out, designed, and implemented, but it would certainly not be as trivial as the case with implicit reference conversions. In fact, I reckon it would be a lot more complicated and very drastic changes to both the language and runtime would be needed, which is probably why this is not implemented.

Why does this conversion involving type parameter work?

From C# in a nutshell
C#’s cast operator can perform several kinds of conversion, including:
Numeric conversion
Reference conversion
Boxing/unboxing conversion
Custom conversion (via operator overloading; see Chapter 4)
The decision as to which kind of conversion will take place happens at
compile time, based on the known types of the operands. This creates
an interesting scenario with generic type parameters, because the
precise operand types are unknown at compile time. If this leads to
ambiguity, the compiler generates an error.
The most common scenario
is when you want to perform a reference conversion:
StringBuilder Foo<T> (T arg)
{
if (arg is StringBuilder)
return (StringBuilder) arg; // Will not compile
...
}
Without knowledge of T ’s actual type, the compiler is
concerned that you might have intended this to be a custom
conversion. ...
A more general solution is to first cast to object . This works
because conversions to/from object are assumed not to be custom
conversions, but reference or boxing/ unboxing conversions. In this
case, StringBuilder is a reference type, so it has to be a
reference conversion:
return (StringBuilder) (object) arg;
Why is "the compiler is concerned that you might have intended
this to be a custom conversion" a problem? Is it because of the
ambiguity that (StringBuilder) arg can be either a reference
conversion or a custom conversion, depending on what the actual type
of arg will be?
In the solution, (object) arg can be either a reference conversion
or a boxing conversion, depending on the actual type of arg. Is this an ambiguity, and whyisn't this ambiguity a problem?
Is it possible that the actual type of arg can't be converted to
StringBuilder directly?
If yes, how can adding a conversion (object) before
(StringBuilder) make it work?
Thanks.

Combining delegates and contravariance

The article states the following: http://msdn.microsoft.com/en-us/library/dd799517.aspx
Variance does not apply to delegate combination. That is, given two delegates of types Action<Derived> and Action<Base> (Action(Of Derived) and Action(Of Base) in Visual Basic), you cannot combine the second delegate with the first although the result would be type safe. Variance allows the second delegate to be assigned to a variable of type Action<Derived>, but delegates can combine only if their types match exactly.
Action<B> baction = (taret) => { Console.WriteLine(taret.GetType().Name); };
Action<D> daction = baction;
Action<D> caction = baction + daction;
In the above code baction and daction take different parameters. But still I am able to combine them.
What am I missing?
TIA.
The documentation is not clear, I agree.
The problem is that the runtime types of two combined delegates must match. Which means that there are scenarios where the compile time types match, but the runtime types do not.
Consider for example:
Func<string> f1 = ()=>"";
Func<object> f2 = ()=>null;
Func<object> f3 = f1; // legal in C# 4 because of covariance
Func<object> f4 = f2 + f3;
That's legal at compile time; you're adding two delegates of the same compile-time type. But at runtime it will fail because the runtime requires that the runtime types exactly match.
This is an unfortunate hole in the CLR type system. I'm hoping we can get it fixed some day, but no promises.

Generics: casting and value types, why is this illegal?

Why is this a compile time error?
public TCastTo CastMe<TSource, TCastTo>(TSource i)
{
return (TCastTo)i;
}
Error:
Cannot convert type 'TSource' to 'TCastTo'
And why is this a runtime error?
public TCastTo CastMe<TSource, TCastTo>(TSource i)
{
return (TCastTo)(object)i;
}
int a = 4;
long b = CastMe<int, long>(a); // InvalidCastException
// this contrived example works
int aa = 4;
int bb = CastMe<int, int>(aa);
// this also works, the problem is limited to value types
string s = "foo";
object o = CastMe<string, object>(s);
I've searched SO and the internet for an answer to this and found lots of explanations on similar generic related casting issues, but I can't find anything on this particular simple case.
Why is this a compile time error?
The problem is that every possible combination of value types has different rules for what a cast means. Casting a 64 bit double to a 16 bit int is completely different code from casting a decimal to a float, and so on. The number of possibilities is enormous. So think like the compiler. What code is the compiler supposed to generate for your program?
The compiler would have to generate code that starts the compiler again at runtime, does a fresh analysis of the types, and dynamically emits the appropriate code.
That seems like perhaps more work and less performance than you expected to get with generics, so we simply outlaw it. If what you really want is for the compiler to start up again and do an analysis of the types, use "dynamic" in C# 4; that's what it does.
And why is this a runtime error?
Same reason.
A boxed int may only be unboxed to int (or int?), for the same reason as above; if the CLR tried to do every possible conversion from a boxed value type to every other possible value type then essentially it has to run a compiler again at runtime. That would be unexpectedly slow.
So why is it not an error for reference types?
Because every reference type conversion is the same as every other reference type conversion: you interrogate the object to see if it is derived from or identical to the desired type. If it's not, you throw an exception (if doing a cast) or result in null/false (if using the "as/is" operators). The rules are consistent for reference types in a way that they are not for value types. Remember reference types know their own type. Value types do not; with value types, the variable doing the storage is the only thing that knows the type semantics that apply to those bits. Value types contain their values and no additional information. Reference types contain their values plus lots of extra data.
For more information see my article on the subject:
http://ericlippert.com/2009/03/03/representation-and-identity/
C# uses one cast syntax for multiple different underlying operations:
upcast
downcast
boxing
unboxing
numeric conversion
user-defined conversion
In generic context, the compiler has no way of knowing which of those is correct, and they all generate different MSIL, so it bails out.
By writing return (TCastTo)(object)i; instead, you force the compiler to do an upcast to object, followed by a downcast to TCastTo. The compiler will generate code, but if that wasn't the right way to convert the types in question, you'll get a runtime error.
Code Sample:
public static class DefaultConverter<TInput, TOutput>
{
private static Converter<TInput, TOutput> cached;
static DefaultConverter()
{
ParameterExpression p = Expression.Parameter(typeof(TSource));
cached = Expression.Lambda<Converter<TSource, TCastTo>(Expression.Convert(p, typeof(TCastTo), p).Compile();
}
public static Converter<TInput, TOutput> Instance { return cached; }
}
public static class DefaultConverter<TOutput>
{
public static TOutput ConvertBen<TInput>(TInput from) { return DefaultConverter<TInput, TOutput>.Instance.Invoke(from); }
public static TOutput ConvertEric(dynamic from) { return from; }
}
Eric's way sure is shorter, but I think mine should be faster.
The compile error is caused because TSource cannot be implicitly cast to TCastTo. The two types may share a branch on their inheritance tree, but there is no guarantee. If you wanted to call only types that did share an ancestor, you should modify the CastMe() signature to use the ancestor type instead of generics.
The runtime error example avoids the error in your first example by first casting the TSource i to an object, something all objects in C# derive from. While the compiler doesn't complain (because object -> something that derives from it, could be valid), the behaviour of casting via (Type)variable syntax will throw if the cast is invalid. (The same problem that the compiler prevented from happening in example 1).
Another solution, which does something similar to what you're looking for...
public static T2 CastTo<T, T2>(T input, Func<T, T2> convert)
{
return convert(input);
}
You'd call it like this.
int a = 314;
long b = CastTo(a, i=>(long)i);
Hopefully this helps.

Contravariant Delegates Value Types

Can anyone shed light on why contravariance does not work with C# value types?
The below does not work
private delegate Asset AssetDelegate(int m);
internal string DoMe()
{
AssetDelegate aw = new AssetDelegate(DelegateMethod);
aw(32);
return "Class1";
}
private static House DelegateMethod(object m)
{
return null;
}
The problem is that an int is not an object.
An int can be boxed to an object. The resulting object (aka boxed int) is, of course, an object, but it's not exactly an int anymore.
Note that the "is" I'm using above is not the same as the C# operator is. My "is" means "is convertible to by implicit reference conversion". This is the meaning of "is" used when we talk about covariance and contravariance.
An int is implicit convertible to an object, but this is not a reference conversion. It has to be boxed.
An House is implicit convertible to an Asset through a reference conversion. There's no need to create or modify any objects.
Consider the example below. Both variables house and asset are referencing the very same object. The variables integer and boxedInt, on the other hand, hold the same value, but they reference different things.
House house = new House();
Asset asset = house;
int integer = 42;
object boxedInt = integer;
Boxing and Unboxing is not as simple as it may look like. It has many subtleties, and might affect your code in unexpected ways. Mixing boxing with covariance and contravariance is an easy way to make anyone dazzle.
I agree with Anthony Pegram's comment - it is based on reference types having a different memory footprint than the value types: the CLR can implicitly use a class of one type as a class of its super type, but when you start using value types, the CLR will need to box your integer so it can work in the place of the object.
If you're looking to make it work anyway, I have a tendency to wrap the declaration in an expression:
AssetDelegate aw = new AssetDelegate((m) => DelegateMethod(m));
I don't know if that's good practice or not as far as syntax goes, but remember that boxing and unboxing is expensive.

Categories

Resources