Saturday, October 29, 2011

“No coercion operator is defined between types” error in expression trees

Suppose that you have 2 inherited classes:

   1: public class Bar
   2: {
   3:     public static explicit operator Bar(string s) { return null; }
   4: }
   5: public class Foo : Bar
   6: {
   7: }

As shown above in the parent class Boo there is explicit cast operator from string (the fact that it returns null is not relevant for us). With this operator you may cast Bar from string:

   1: Bar b = (Bar)"foo";

Also you may cast to Foo and compiler will allow to do that:

   1: Foo f = (Foo)"foo";

Now let’s try to construct expression tree which converts string to Foo:

   1: var expr = Expression.Convert(Expression.Constant("foo"), typeof (Foo));

It will compile but will throw exception “No coercion operator is defined between types 'System.String' and 'Foo'”. This looks strange because compiler allows to do the same cast directly.

Ok, there is overridden version of Convert method and if you checked my previous post about similar problem with LessThan method, we can try to use it in order to avoid the problem:

   1: var mi = typeof(Bar).GetMethod("op_Explicit");
   2: var expr = Expression.Convert(Expression.Constant("foo"), typeof(Foo), mi);

Unfortunately no luck here as well: it throws exception “The operands for operator 'Convert' do not match the parameters of method 'op_Explicit'”.

If you will try to get op_Explicit using Foo type:

   1: var mi = typeof (Foo).GetMethod("op_Explicit");

you will again get “No coercion operator is defined between types 'System.String' and 'Foo'”” error. The same behavior exist both in .Net 3.5 and 4.0. This problem looks like a bug in expression trees.

5 comments:

  1. Hi! I am having this problem with "System.Guid" instead of foo...

    The line of code is:
    Expression.Convert(Expression.Constant("70bedc67-f415-4948-8156-52742f1b472b"), typeof(Guid))

    How do I solve it?
    Thanks! Sophie

    ReplyDelete
  2. The exception I get is: "No coercion operator is defined between types 'System.String' and 'System.Guid'."

    Thanks! Sophie

    ReplyDelete
  3. hello Sophie,
    in your case exception is correct. You try to construct convert expression from string to Guid, which is not allowed. If you will compile the following code:
    Guid guid = (Guid)"70bedc67-f415-4948-8156-52742f1b472b";

    you will get compilation error: "Cannot convert type 'string' to 'System.Guid'". When you create expression Expression.Convert(Expression.Constant("70bedc67-f415-4948-8156-52742f1b472b"), typeof(Guid)) - you make the same thing.

    If you want to get Guid from string, you need to write the following code:
    Guid guid = new Guid("70bedc67-f415-4948-8156-52742f1b472b");

    it will be compiled and executed properly. So your expression should be different as well:
    var expr = Expression.New(typeof (Guid).GetConstructor(new[] {typeof (string)}), Expression.Constant("70bedc67-f415-4948-8156-52742f1b472b"));

    It will construct expression which creates Guid from string.

    ReplyDelete
  4. Alex, i think you are wrong.
    In you case you need additional convert call to make it work properly i.e.:
    using E = System.Linq.Expressions.Expression;
    E.Convert(E.Convert(E.Constant("foo"), typeof (Bar)), typeof(Foo));

    ReplyDelete
  5. hi Alexander,
    the post is about different problem. Yes I know about that workaround, but we needed it without additional convert. This is how I used this workaround in the real code:
    Expression.Convert(Expression.Convert(Expression.Constant("foo"), typeof(BaseFieldType)), typeof(DataTypes.DateTime))

    But this is not what we needed. We needed direct convert from "foo" to DataTypes.DateTime, because the code above produced expression tree which then will be passed to expression2code lib which will create C# uncompilable code based on it:
    (DataTypes.DateTime)(BaseFieldType)"foo"
    while we need:
    (DataTypes.DateTime)"foo"
    BaseFieldType - is internal class in our case, that's why code is not compiled. So we needed to add ugly workaround and replace "(BaseFieldType)" with empty string in output because of this bug. I still think that this is a bug in expression trees, because it doesn't allow you to create expression for the correct C# statement.

    ReplyDelete