Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InvalidOperationException in release-mode of visual studio since using .Net 4.0

I have some trouble to port an existing .NET 3.5 Application to .NET 4.0. The Code isn't written by myself so I didn´t know in detail why the things are as they are.

This is the Situation: Code works fine if the application is started from Visual Studio (Release or Debug-Mode doesn’t matter) and also if the application is started form Debug-folder The Problem is the Release-deploy, because is doesn’t work well since 4.0 (and also in 4.5) :-/

This is the initial call:

someObject.Text = Elements.GetElement(Int16.Parse(cb1.Text));

And Here is the code:

public class Elements : EnumBase<int, Elements>
{
    public static readonly Elements Element1 = Create("Number 0", 0);
    public static readonly Elements Element2 = Create("Number 1", 1);

    private static Elements Create(string text, int value) 
    {
        return new Elements() { text = text, value = value };
    }

    public static String GetElement(int id)
    {

        // The Following Code safes the day and let the release deploy work fine.
        // It doesn´t matter if the condition becomes true or not to runtime.
        /* 
        if (id == 999999999)
        {
            Elements el = Element1;
        }
        */

        // Release deploy works also fine if you do the following line in a loop instead of linq.
        return BaseItemList.Single(v => v.Value == id).Text; 
    }
}

[Serializable()]
public class EnumBase<T, E> :  IEqualityComparer<E> 
        where E : EnumBase<T, E>
{
    private static readonly List<E> list = new List<E>();
    protected string text;
    protected T value;

    protected static IList<E> BaseItemList
    {
        get
        {
            return list.Distinct(new EnumBase<T, E>(false)).ToList();
        }
    }

    protected EnumBase()
    {
        list.Add(this as E);
    }

    /// <summary>
    /// Constructor for distinct to avoid empty elements in the list
    /// </summary>   
    private EnumBase(bool egal) {}

    public string Text
    {
        get { return text; }
    }

    public T Value
    {
        get { return value; }
    }


    #region IEqualityComparer<E> Member

    // ...

    #endregion
}

The key is return BaseItemList.Single(v => v.Value == id).Text;. It throws a InvalidOperationException, because in Release public static readonly Elements Element1 = Create("Number 0", 0); and public static readonly Elements Element2 = Create("Number 1", 1); aren't ready. In the moment of the Exception is BaseItemList empty (BaseItemList.Count = 0). I am not sure why this happened in release form bin-folder and not in release out of visual studio. For tests I deactivated "Optimize code" in project-properties but it doesn’t help.

Surely the construct isn't the best, but I want to know what is different in .Net 4.0 that bring the code to flatter.

Thanks for help

like image 747
Micha Avatar asked Jan 24 '13 07:01

Micha


People also ask

How do I release mode in Visual Studio?

In Solution Explorer, right-click the project and choose Properties. In the side pane, choose Build (or Compile in Visual Basic). In the Configuration list at the top, choose Debug or Release. Select the Advanced button (or the Advanced Compile Options button in Visual Basic).

Can build debug but not Release?

If an application works in a debug build, but fails in a release build, one of the compiler optimizations may be exposing a defect in the source code. To isolate the problem, disable selected optimizations for each source code file until you locate the file and the optimization that is causing the problem.

What is difference between debug and release in Visual Studio?

By default, Debug includes debug information in the compiled files (allowing easy debugging) while Release usually has optimizations enabled. As far as conditional compilation goes, they each define different symbols that can be checked in your program, but they are language-specific macros.

What is release mode in C#?

Release Mode: When we are going to production mode or deploying the application to the server. 2). Code optimization. Debug Mode: The debug mode code is not optimized. Release Mode: The release mode code is optimized.


1 Answers

I believe the problem is that you're relying on the static initializer for Elements having run, despite the fact that you haven't referred to any fields within it. The type initializer in a type which doesn't have a static constructor is only guaranteed to run before the first static field access. Section 10.5.5.1 of the C# 5 specification:

If a static constructor (§10.12) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.

And section 10.12 has:

The static constructor for a closed class type executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • An instance of the class type is created.
  • Any of the static members of the class type are referenced.

The implementation of type initialization changed in .NET 4, but it was only an implementation detail - your code was broken before, you just didn't know it.

If you change your code to have:

static Elements() {}

in the Elements class, then I believe it will work - because static constructors force type initialization to occur before immediately the first member access, rather than just "at some point before the first field access".

Personally I'm dubious about the general pattern, but that's a slightly different matter.

like image 121
Jon Skeet Avatar answered Oct 19 '22 20:10

Jon Skeet