13

構造体を取り、不変のラッパーと、新しいインスタンスを段階的に構築するための「ビルダー」クラスを自動的に生成するツール/ライブラリは何ですか?

入力例:

struct Foo
{
    public int apples;
    public int oranges;
    public Foo Clone() {return (Foo) base.MemberwiseClone();}
}

出力例:

public class ImmutableFoo // could probably be a struct
{
    private Foo snapshot;
    internal ImmutableFoo(Foo value) { this.snapshot = value; }
    public FooBuilder Builder() { return new FooBuilder(snapshot); }
    public int Apples { get { return snapshot.apples; } }
    public int Oranges { get { return snapshot.oranges; } }
}

public class FooBuilder
{
    private Foo state;

    public int Apples { get { return state.apples; } set { state.apples = value; } }
    public int Oranges { get { return state.oranges; } set { state.oranges = value; } }

    public FooBuilder() { }

    internal FooBuilder(Foo toCopy) { state = toCopy.Clone(); }

    public ImmutableFoo Build()
    {
        ImmutableFoo result = new ImmutableFoo(state);
        state = state.Clone();
        return result;
    }
}

このような「ツール」は、IDE プラグインであるか、リフレクションを使用して実行時に新しいクラスを生成することができます。

例は C# ですが、静的に型付けされた OO 言語 (Java、Scala、C++ など) のソリューションに興味があります。

望ましい機能:

  • ビルダー クラスの構造体からメソッドを再作成します。
  • 不変クラスの構造体から非破壊的なメソッドを再作成します (特にEquals()andGetHashCode()およびインターフェイス メソッド) 。
  • IFooReaderまた、不変とビルダーの両方によって実装される、各構造体メンバーの読み取り専用プロパティを含むインターフェイスを生成します。
  • フィールドのクラスに不変の同等物がある場合、不変のクラスで不変のバージョンを使用します (参照型であるプロパティを持つオブジェクトの C# でビルダーを作成する方法も参照してください)。eg List->ReadOnlyCollectionなど。
  • または、ビルダー クラスを入力として使用します (ビルダーは、構造体に委譲する代わりに自動プロパティを使用します)。
  • Cloneメソッドを事前定義する必要はありません

「このようなツールは使用しないでください...」という回答も大歓迎です。

4

2 に答える 2

4

これが4つの可能な解決策です。

1)CodeDOMを使用してC#またはVBコードを生成します。これにより、Visual Studio拡張機能を使用して、デザイナーファイルにコードを生成することもできます。Visual Studioがすでに提供している組み込みツールのいくつかに似ています。たとえば、Webサービス呼び出しのラッパーを生成するツールなどです。残念ながら、VisualStudioの拡張についてはよくわかりません。

  • 長所-ビルドする前にソースを生成できます。これにより、任意のアセンブリから生成された型に対してコードを簡単に記述できます。
  • 短所-言語に依存しません。あなたはサポートされている言語で立ち往生しています。

2)Mono.Cecilライブラリを使用して、ビルド後のアセンブリを分析します。その後、新しいタイプを含めてアセンブリを書き直すことができます。

  • 長所-言語に依存しません。
  • 短所-構造体が定義されている同じアセンブリに型を追加すると、同じアセンブリで生成された不変の構造体型に対してコードを記述できなくなります。生成されたタイプを新しいアセンブリに配置すると、これが可能になります。

3)PostSharpを使用します。このライブラリについてはよくわからないので、アセンブリに新しいタイプを追加できない可能性がありますが、メソッドにILを挿入することはできます。また、属性を使用してこれを簡単に実行できる優れた機能もたくさんあります。だからあなたはこれを行うことができます-

[GenerateImmutable]
struct Foo
{
    public int apples;
    public int oranges;
    public Foo Clone() {return (Foo) base.MemberwiseClone();}
}
  • 長所-言語に依存せず、 PostSharpでAOPを実行する方が簡単です。
  • 短所-Mono.Cecilと同じですが、PostSharpを使用して新しいタイプを生成できるかどうかもわかりません。

4)組み込みのReflection.Emitライブラリを使用して、不変の型で新しいアセンブリを生成します。

  • 長所-言語に依存せず、サードパーティのものはありません。
  • 短所-生成されたタイプを新しいアセンブリに配置する必要があります。元のタイプと同じアセンブリにそれらを追加することはできません。
于 2010-02-12T02:22:52.483 に答える
1

なぜビルダーを気にするのですか?

(厄介な)変更可能な構造体がありますが、面倒で不要なビルダーを作成するのではなく、それを直接使用する必要がある場合。

この種のラッパーを自動生成する必要があると感じるのに十分な数のこれらの構造体があることは、やや気になります。私の腸の反応は、あなたが間違ったことをしているということです...

不変ラッパーの目的がスナップショットを保存することだけである場合は、次のようなものを使用してください。

public struct Snapshot<T> where t : struct
{
    private readonly T data;
    public Snapshot(T value) { this.data = value; }
    public T Data { get { return data; } }
}

渡された構造体は二度と変更されないことが保証されていますが、構造体のすべての値に直接アクセスできます (これらの結果の変更は、基になる get_Data 関数を呼び出したときに作成されたコピーで発生します)。

于 2010-02-13T18:00:14.570 に答える