18

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

4

1 に答える 1

21

問題は、そのElements中のフィールドを参照していないという事実にもかかわらず、実行のために静的初期化子に依存していることだと思います。静的コンストラクターを持たない型の型初期化子は、最初の静的フィールド アクセスの前にのみ実行されることが保証されています。C# 5 仕様のセクション 10.5.5.1:

静的コンストラクター (§10.12) がクラスに存在する場合、静的フィールド初期化子の実行は、その静的コンストラクターを実行する直前に発生します。それ以外の場合、静的フィールド初期化子は、そのクラスの静的フィールドを最初に使用する前に、実装に依存する時点で実行されます。

セクション 10.12 には次の内容があります。

クローズ クラス型の静的コンストラクターは、特定のアプリケーション ドメインで最大 1 回実行されます。静的コンストラクターの実行は、アプリケーション ドメイン内で次のイベントの最初の発生によってトリガーされます。

  • クラス型のインスタンスが作成されます。
  • クラス型の静的メンバーのいずれかが参照されています。

型の初期化の実装は.NET 4で変更されましたが、それは実装の詳細に過ぎませんでした。コードは以前に壊れていましたが、それを知らなかっただけです。

コードを次のように変更した場合:

static Elements() {}

Elementsクラスでは、それ機能すると思います-静的コンストラクターは、「最初のフィールドアクセスの前のある時点」ではなく、最初のメンバーアクセスの直前に型の初期化を強制するためです。

個人的には一般的なパターンは半信半疑なのですが、それはちょっと違います。

于 2013-01-24T07:17:33.757 に答える