2010-11-18 13 views
3

クラス定義を自分の実装に置き換える(または上書きする)ことはできますか?C#でのクラス実装の置き換え

説明:

  • 私はSystem.Randomインスタンスを持つクラスを持っている、と私は、元のコードを変更せずにテストにそれをしようとしています...
  • だから私はランダムに置き換えることを計画していますクラスを生成したシーケンスを制御することができます。this questionを参照してください。

`

class foo 
{ 
    Random r=new Random(); 
    double bar() 
    { 
     double ans=r.NextDouble(); 
     .....// evaluate ans 
     return ans; 
    } 
} 

`

(または最小限の変更で)元のコードファイルを変更せずに、私は別のもので、ランダムクラスの実装を置き換えることができます可能な方法は何ですか?

編集:
は 一つの解決策は、ランダムクラスを継承し、それを変更することです...しかし、それは継承されたものとそれぞれランダムインスタンスを変更する必要があり、私はちょうどそれをテストしていので...私は、コードを変更する必要はありません!!
編集2:
C#で言うことができます: "このファイルの各ランダムなクラスは、MyNamespace.Random System.Random"ではありませんか?

+0

あなたは明確にすることはできますか?それがメンバーの場合は、一部を交換するだけですか?あなたは新しいものを追加するためにそれを拡張したいですか? – CodingGorilla

+1

あなたはモックを作成した後のように聞こえるでしょうか? – Lucero

+0

@Lucero +1、私はこの機能を使用して生成される乱数を設定しているのでNextDouble()を呼び出す前に、このランダムクラスに関数setNextSequence(ダブル[])を追加することが心配です同じこと – CodingGorilla

答えて

3

これを行うことができます。ソースにコードを変更する必要はありません。

あなたはモックまたは派生インスタンスを注入するためにリフレクションを使用することができます。

System.Randomsealedではないので、ここでは新しいクラスを派生させ、必要なメソッドをオーバーライドするだけです。

私はあなたのユニットテストフレームワークのいくつかのタイプを使用していることを仮定しているので、以下の例はそのためです。あなたのユニットテストファイルで

、次の操作を行います。

class MyRandom : System.Random 
{ 
    public override double NextDouble() 
    {  
      // return your fake random double here. 
    } 
} 


class TestFixture 
{ 
    public void UnitTest() 
    { 
      // Create the unit under test 
      foo unit = new foo(); 

      // use reflection to inject a mock instance 
      typeof(foo) 
      .GetField("r", BindingFlags.Instance | BindingFlags.NonPublic) 
      .SetValue(unit, new MyRandom()); 

      // ACT: call method 
      var result = foo.bar(); 

      // examine results 
     } 
} 
+0

Wow ..で与えられています。これは私が必要としているものです...私はこれがプライベートフィールドでうまくいくのだろうか? – Betamoo

+0

ええ、問題ありません。 'BindingFlags'の指定方法によって異なります。 –

+0

これはどこでも動作しますか?つまり、.NET Frameworkでクラスインスタンスを作成し、新しいRandom()を使用すると、MyRandomのインスタンスが取得されますか? – paulm

0

Dependency Injectionをご覧ください。このような状況のために設計されています。 C#のいくつかの異なるDI/IoCフレームワークがあります。

UnityとNinjectは両方とも優れていますが、他にも多数あります。

5

編集:ChaosPandionが指摘しているように、メソッドのロジックをテストするには、doubleパラメータ、つまりdouble bar(double input) { ... }を使用する方がよいでしょうが、独自のランダムソースを注入する必要がある場合は、これらのアプローチの:

あなたがから値を取得するためにFunc<double>を取るためにバーを変更できます。そして、あなたがテストのために、メインプログラムの使用に独自の実装を注入することができ

double bar(Func<double> source) 
{ 
    double ans = source(); 
    //evaluate ans 
    return ans; 
} 

Random r = new Random(); 
double ans = bar(r.NextDouble); 

代わりに独自のIRandomインターフェースを作成することができ、その後、実際のRandomクラスの周りにラッパーを実装し、使用することは、テストのためにモック:

interface IRandom 
{ 
    double Next(); 
} 

public class RandomWrapper : IRandom 
{ 
    private Random r = new Random(); 
    public double Next() 
    { 
     return this.r.NextDouble(); 
    } 
} 
+1

私はあなたに投票しましたが、質問する必要があります。パラメータとしてダブルを使用してみませんか? – ChaosPandion

0

あなたは抽象化の層を追加する必要があります。

ランダムクラスがサポートする必要がある契約を定義するインターフェイスを作成します。

そのインターフェイスの実装を作成します。そのインターフェイスの1つは元のRandomクラスを使用する必要があり、もう1つは "ランダム"実装です。

必要なときに適切な実装を行います。

2

JustMockまたはTypeMockなどのツールを使用して、Randomのようなクラスの実装を置き換えることができると私は信じています。

しかし、依存性注入を使用して(あるいは、抽象化の層を挿入)するために、あなたの元のコードを再構築することなく、あなたはプレーンなC#を使用して求めているものを行うことはできません。つまり、ハード依存関係を取り除くためにコードを再構築すれば、コードはよりクリーンでシンプルになるので、必要な再構築を本当に行うことができない限り、優先ルートにする必要があります。

1

System.Randomからクラスを派生させ、そのメソッドをオーバーライドできます。

class PseudoRandom : System.Random 
{ 
    public override int Next() {...} 
    public override int Next (int maxValue) {...} 
    public override int Next (int minValue, int maxValue) {...} 
    public override void NextBytes (byte [] buffer) {...} 
    public override double NextDouble() {...} 
} 

メインからインスタンス化するには、このクラスを使用します。

Random random = new PseudoRandom(); 

システムクラスまたは独自の派生バージョンを使用するかどうかを後で判断する必要がある場合は、「is」演算子を使用して確認できます。

// We need to see which class we're working with 
if (random is PseudoRandom) 
{ 
    // Using our custom class 
} 
else 
{ 
    // Using default System.Random class 
} 
+0

良いアイデア..しかし、私はC#で言うことができます: "このファイルの各ランダムなクラスは、MyNamespace.RandomないSystem.Random"ですか? – Betamoo

+0

はい、is演算子で確認してください。更新された応答。 –

0

回答、複雑なビット(おそらくない最高のデザインは、それが迅速かつ簡単です)、それは意図私見にビット明確

public class Foo 
{ 
    Func<double> next; 
    Foo(bool use_new) 
    { 
     if(use_new) 
      next = (new MyRandom()).NextDouble; 
     else 
      next = (new Random()).NextDouble;    
    } 
    void bar() 
    { 
     ans = next(); 
     ... 
    } 
} 

回答Bを、作品。関数を返す共通のインタフェースを定義する。次に、使用する機能を選択します。

public interface IRandomProvider 
{ 
    Func<double> NextDouble { get; } 
} 

public class StdRandomProvider : IRandomProvider 
{ 
    Random r = new Random(); 
    Func<double> NextDouble 
    { 
     get { return()=>r.NextDouble(); } 
    } 
} 

public class MyRandomProvider : IRandomProvider 
{ 
    MyRandom r = new MyRandom(); 
    Func<double> NextDouble 
    { 
     get { return()=>r.MyNextDouble(); } 
    } 
} 

public class Foo 
{ 
    IRandomProvider r; 
    Foo(bool use_new) 
    { 
     if(use_new) 
      r= new MyRandomProvider(); 
     else 
      r= new StdRandomProvider();    
    } 
    void bar() 
    { 
     ans = r.NextDouble(); 
     ... 
    } 
} 
+0

私はちょうど無作為が '密封されていない 'ことを認識したので、答えがAK – ja72

関連する問題