1

Last week I've installed the Visual Studio 2015 Enterprise edition (further named: VS2015). I want to perform some test before switching to the newer version. For example backwards compatibility of the project files. I'm ignoring currently all C# 6.0 features, because not all team members have installed their updates to support C# 6.0.

My problem is, that a solution/project which is built with VS2015 has a different behavior in case of exception throwing to the same solution/project which was built with VS2013 (Premium).

Following I'm going to describe the scenario with the code snippets which are failing when built with VS2015.

The behavior of the project built with VS2015 changed as follows: The code below is throwing an System.ArgumentException with following message: Static method requires null instance, non-static method requires non-null instance. Parameter name: method

Code sippets

//A delegate to convert string values from an array (e3k.Values) to the property type of the desired object.
Func<TE3K, int, Type, object> stringFromDictionary = (e3k, index, type) =>
{
    //handle enums
    if (type.IsEnum)
    {
        if (typeof (TE3K) == typeof (VoucherHeader)) //maybe not so good
            return VoucherEnumConverter.ToWebEnum(e3k.Values[index], type, e3k.Values);

        return DefaultEnumConverter.ToWebEnum(e3k.Values[index], type);
    }

    //set default value, false if api sends null
    if (type == typeof(Boolean) && e3k.Values[index] == null)
         return false;

    //fix exception when a value like "0" or "1" is converted to boolean.
    if (type == typeof(Boolean) && (e3k.Values[index] == "0" || e3k.Values[index] == "1"))
        return Convert.ChangeType(Int32.Parse(e3k.Values[index]), type, CultureInfo.InvariantCulture);

    //handle nullables
    if (Nullable.GetUnderlyingType(type) != null)
          return Convert.ChangeType(e3k.Values[index], Nullable.GetUnderlyingType(type), CultureInfo.InvariantCulture);

    return Convert.ChangeType(e3k.Values[index], type, CultureInfo.InvariantCulture);
};
MethodInfo getStrValueFromDict = stringFromDictionary.Method;

Description: The object TE3K is an object from an external .dll which I have to convert into an object I can work with. This object of type TE3Khas an array with its values obviously they're all Strings. The parameter intprovides the index for the array (e3k.Values[i]) The parameter Typeis the PropertyTypewhich the string value must be converted into.

//get mapper, sets the index needed in `e3k.Values[index]` for each property of `TWeb`
Dictionary<PropertyInfo, int> mapper = Converter.GetMapper<TE3K, TWeb>();
foreach (var property in mapper)
{
    Expression propertyValue = default(Expression);
    if (property.Key.PropertyType == typeof(DateTime)) //not relevant for this case
    {
        propertyValue = Expression.Call(getDateValueFromDict, r, Expression.Constant(property.Value));
    }
    else if (property.Key.PropertyType == typeof(DateTime?)) //not relevant in this case
    {
        propertyValue = Expression.Call(getDateValueFromDictSave, r, Expression.Constant(property.Value));
    }
    else
    {
        //exception is thrown on this line but only when project is compiled in VS2015
        propertyValue = Expression.Call(getStrValueFromDict, r, Expression.Constant(property.Value), Expression.Constant(property.Key.PropertyType)); 
    }
    UnaryExpression boxedValue = Expression.Convert(propertyValue, property.Key.PropertyType);
    bindings.Add(Expression.Bind(property.Key, boxedValue));
    FieldList.Add(property.Value);
}

I now what causes this exception under normal conditions. It's thrown when you make a call to a non static method inside the delegate which is passed into the first parameter of Expression.Call(_delegate, args, ...) or if one of the arguments is a non constant value. (Correct me if I'm wrong here, that is how I understand this.)

Summary

Obviously I'm pretty confused by this behavior. The exception mentioned earlier only occurs when the project is built with VS2015. When built with VS2013the code runs as expected. Are there differences in how visual studio builds and optimizes code between those two versions?

What I've tried

  • Compiling on Release/Debug both don't change the behavior.
  • Changing C# version does also change nothing.

Feel free to explain extensively if you know a cause. If you need information please ask, I provide them if available.

Sinya
  • 154
  • 3
  • 15
  • *Changing C# version does also change nothing.* Yes, because [it doesn't do what one expects it to do](http://ericlippert.com/2013/04/04/what-does-the-langversion-switch-do/) – Sriram Sakthivel Aug 21 '15 at 07:11
  • @SriramSakthivel Nice to know. I was trying to play with some settings to see if the behavior changes, but I wasn't really expecting that they change something. – Sinya Aug 21 '15 at 08:40

1 Answers1

1

The error is thrown if getStrValueFromDict is non-static, not if you have non-static calls inside it like you've written. You need to handle the case that getStrValueFromDict is compiled to an instance method. This could definitely be different based on the used compiler.

Expression instance = getStrValueFromDict.IsStatic ? null : Expression.Constant(stringFromDictionary.Target);
propertyValue = Expression.Call(instance, getStrValueFromDict, r, Expression.Constant(property.Value), Expression.Constant(property.Key.PropertyType));

Here is some background information about why some delegates are now compiled to instance methods: https://stackoverflow.com/a/30897727/631802

Community
  • 1
  • 1
cremor
  • 6,669
  • 1
  • 29
  • 72
  • Nice, Thank you. That solved this issue. I think I should read a little bit more about the Roslyn compiler. – Sinya Aug 21 '15 at 11:23