Does Expression.Convert not throw an InvalidOperationException for invariant value type parameters?

Expression.Convert usually throws an InvalidOperationException when "the expression operator is not defined between the expression. Type and type."

The return type parameter Func<> is covariant for reference types.

 // This works. Func<SomeType> a = () => new SomeType(); Func<object> b = a; 

It is not covariant for value types .

Deviation applies only to reference types; if you specify a value type for a variant type parameter, this parameter type is invariant for the resulting constructed type.

 // This doesn't work! Func<int> five = () => 5; Func<object> fiveCovariant = five; 

However, Expression.Convert believes this is possible.

 Func<int> answer = () => 42; Expression answerExpression = Expression.Constant( answer ); // No InvalidOperationException is thrown at this line. Expression converted = Expression.Convert( answerExpression, typeof( Func<object> ) ); 

No InvalidOperationException is InvalidOperationException when Expression.Convert called. The expression tree compiles correctly, but when I call the created delegate, I get the expected InvalidCastException .


It seems that all rejection logic is not properly supported. He correctly complains about the impossibility of converting from Func<SomeType> to Func<SomeOtherType> , but he does not complain about converting from Func<object> to Func<string> .

Interestingly, once SomeType and SomeOtherType are in the same class hierarchy ( SomeOtherType continues from SomeType ), it never throws an exception. If this is not so, then.

+5
source share
3 answers

This is mistake?

Yes. The expression tree library was probably not constantly updated when we added covariance and contravariance. Sorry.

I reported this as a bug in Microsoft Connect.

Thanks! Then someone will look at him.

How to check if a type can be converted to another type?

The question is unclear. For two type objects you want to know:

  • Does the .NET runtime consider type compatibility compatible?
  • C # compiler thinks that there is an implicit conversion between types?
  • C # compiler thinks there is an explicit conversion between types?

"int" and "short", for example, are not an assignment compatible with .NET rules. Int is explicitly convertible, but is implicitly converted to short, and short is both implicitly and explicitly converted to int according to C # rules.

+7
source

It's not a mistake. Expression.Convert provides a runtime type check, so an InvalidCastException at runtime will be the expected behavior.

Edit: this is not entirely correct. It certainly does not represent a runtime type check (here is the documentation: http://msdn.microsoft.com/en-us/library/bb292051.aspx ). However, an expression tree is created at runtime, so all type checks must be performed then.

Edit: I am also using .NET 4.0.

By the way, Convert does not complain about the conversion from Func<object> to Func<string> , because this conversion is sometimes legal. This is legal if Func<object> is a covariant reference to an object whose execution type is Func<string> . Example:

 Func<string> sFunc = () => "S"; Func<object> oFunc = sFunc; Func<string> anotherSFunc = (Func<string>)oFunc; 

Convert now decides whether an InvalidOperationException should be thrown, checking if one type can be forced to another. When checking delegates for potential link conversions, it looks like the code is checking contravariant (argument) parameters and InvalidOperationException if there is any type of value. This does not seem to do a check for the covariant (return type) parameter. Therefore, I begin to suspect that this is a mistake, although I am inclined to leave a judgment on this matter until I have the opportunity to look at the specification (see Eric Lippert Maybe something is wrong with the universe, but probably not ), what I don’t have time to do right now.

+1
source

Eric Lippert answered part 1 of my question: it seems to be a mistake. I started looking for a solution to question 2:

How to check if a type can be converted to another type?

I just make my first attempt in the Type.CanConvertTo( Type to ) method in my library . (Sources are too complicated to post here, sorry.)

 if ( fromType.CanConvertTo( toType ) ) { convertedExpression = Expression.Convert( expression, toType ); } 

While it supports checking for implicit conversions for:

  • Simple non-generic types.
  • Deviation for nested common interfaces and delegates.
  • Invariance for parameters of a type type value.

It does not support:

  • Type restrictions.
  • Custom implicit conversion operators.

It passes all my tests for implicit conversions . Although you can specify explicit conversions as well (and I actually use it as of now ), I still need to write unit tests for all of these scenarios.

0
source

Source: https://habr.com/ru/post/900807/


All Articles