2016-09-09 5 views
0

シングルトンのような静的メンバーを持つクラスがあるとします。 私はまた、そのクラスの構築を許可します。 コンシューマが既知の引数を使用してコンストラクトを作成する場合、新しいコンストラクションの代わりに静的なメンバを返したいと思います。コンストラクタ内の静的クラスメンバーの 'this'ポインタの切り替え

以下は、ポイントを示す非コンパイルのコード例です。 本来、クラスはConstructメソッドのように構築されていますが、クラスのコンシューマが既存のnew MyClass(0)呼び出しを書き直すことを強いられないようにしたいと思います。

これは可能ですか?

public class MyClass 
{ 
    public static readonly MyClass Zero = new MyClass(0); 

    private int n; 

    public static MyClass Construct(int n) 
    { 
     MyClass self = new MyClass(0, true); //the bool just helps with referencing 
     ReplaceIfZero(ref self); 
     return self; 
    } 

    public MyClass(int n, bool x) //the bool is just so i can reference it in this example 
    { 
     this.n = n; 
    } 

    public MyClass(int n) 
    { 
     this.n = n; 
     ReplaceIfZero(ref this); 
     // Error CS1605 Cannot pass 'this' as a ref or out argument because it is read - only 
    } 

    ReplaceIfZero(ref MyClass myclass) 
    { 
     if(Zero.Equals(myclass)) 
     { 
      myclass = Zero; 
     } 
    } 

    public override bool Equals(object obj) 
    { 
     return ReferenceEquals(this, obj) || ((obj is MyClass) && n.Equals((obj as MyClass).n)); 
    } 
} 
+5

'new'演算子の結果を変更することはできません。パブリックコンストラクタを削除し、クライアントに 'MyClass.Create'のようなpublic static factoryメソッドを使用させる必要があります –

+0

@IvanStoev:あなたのコメントから回答を作成する必要があります – Falanwe

答えて

2

通常の解決策は、コンストラクタをプライベートにして、物事を処理するpublic static factoryメソッドを追加することです。私はそれが最善のアプローチだと思う。

しかし、もう1つのアプローチは、インターフェイスとラッパークラスを使用して詳細を隠すことです。

このような何か:

public interface IMyClass 
{ 
    int SomeMethod(int x); 
} 

public class MyClass: IMyClass 
{ 
    public MyClass(int n) 
    { 
     _impl = MyClassImpl.Create(n); 
    } 

    public int SomeMethod(int x) 
    { 
     return _impl.SomeMethod(x); 
    } 

    // For test purposes only - see later in this answer. 
    public bool ImplementationEquals(MyClass other) 
    { 
     return ReferenceEquals(_impl, other._impl); 
    } 

    readonly IMyClass _impl; 
} 

internal class MyClassImpl : IMyClass 
{ 
    static readonly ConcurrentDictionary<int, IMyClass> _dict = new ConcurrentDictionary<int, IMyClass>(); 

    readonly int _n; 

    MyClassImpl(int n) 
    { 
     _n = n; 
    } 

    public static IMyClass Create(int n) 
    { 
     return _dict.GetOrAdd(n, i => new MyClassImpl(i)); 
    } 

    public int SomeMethod(int x) 
    { 
     return _n + x; 
    } 
} 

そして、クライアントコードはMyClassのインスタンスを作成することができ、そして舞台裏で別のクラスを実装するために使用されています。

クライアントコードは、MyClassの2つのインスタンスを作成した場合、このコードが示すように、実装するオブジェクトは、共有されます。

var a = new MyClass(1); 
var b = new MyClass(2); 
var c = new MyClass(1); 

Console.WriteLine(a.ImplementationEquals(b)); // Prints false 
Console.WriteLine(a.ImplementationEquals(c)); // Prints true 

はまた、あなたがこのような何かをすれば、それはクラスであることが非常に重要だということに注意してください非常に不変です(つまり、すべてのフィールドが不変です。フィールドがプリミティブ型でない場合、すべてのフィールドは再帰的に不変です)

1

私はコメントで述べたように、C#のは、あなたがnew演算子の結果を変更することはできません。常にclassの新しいインスタンスを返します。もちろん、私たちは普通のクラスについて話していますが、ContextBoundObjectや代理のような特別なハックはありません。

したがって、お客様はnew演算子を使用してクライアントを維持し、目的の動作を得る方法はありません。パブリックコンストラクタを削除して、クライアントにMyClass.Create(または、例ではConstruct)のようなpublic static factoryメソッドを使用させる必要があります。

1

あなたのコードから理解している限り、ユーザーが別のインスタンスに対して同じ番号を作成した場合、同じオブジェクトを提供する必要があります。その場合は、工場のパターンやシングルトンのパターンを使用する方がよいでしょう。

1

あなたのクラスのインスタンスは1つだけであるようです。

静的クラスを作成する方が簡単でしょうか?

public static class MyClass 
{ 
    static MyClass() 
    { 
     n_ = 2; 
    } 

    private static int n_; 

    public static void Construct(int number) 
    { 
     n_= number; 
    } 
} 
関連する問題