簡體   English   中英

用C#替換類實現

[英]Replacing a class implementation in C#

我可以用自己的實現替換(或覆蓋)類定義嗎?

說明:

  • 我有一個帶有System.Random實例的類,我試圖在不修改原始代碼的情況下測試它...
  • 所以我打算用我自己的一個實現替換Random類,它允許控制生成的序列..請看這個問題

`

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

`

有什么方法可以用另一個替換Random類的實現而不改變(或用最小的改動)原始代碼文件?

編輯:
一種解決方案是繼承Random類並對其進行修改......但這需要使用繼承的實例更改每個Random實例...我不想修改代碼,因為我只是測試它!
編輯2:
我可以在C#中說:“這個文件中的每個Random類都是MyNamespace.Random而不是System.Random”?

編輯:正如ChaosPandion所指出的,為了測試方法中的邏輯,最好采用雙參數即double bar(double input) { ... } ,但如果你有理由需要注入自己的源randoms,您可以使用以下方法之一:

您可以修改bar以獲取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();
    }
}

您可以執行此操作,並且不需要對源進行代碼更改。

您可以使用反射來注入模擬或派生實例。

由於System.Random沒有sealed ,這里最好的選擇是派生一個新類,然后你只需要覆蓋所需的方法。

我假設你正在使用某種類型的單元測試框架,所以下面的例子是為了這個。

在您的單元測試文件中,執行以下操作:

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
      }
 }

我相信你可以使用JustMockTypeMock等工具替換像Random這樣的類的實現。

但是,如果不重構原始代碼以使用依賴注入(或以其他方式插入抽象層),您將無法使用普通C#執行您所要求的操作。 也就是說,重構代碼以擺脫硬依賴會使代碼更清晰,更簡單,所以我會說它應該是首選路由,除非你真的無法進行必要的重組。

您可以從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 () {...}
}

使用此類從Main實例化。

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
}

看一下使用依賴注入 它專為這種情況而設計。 c#有幾種不同的DI / IoC框架。

Unity和Ninject都很好,但還有很多其他的。

您需要添加一個抽象層。

創建一個接口,定義您需要隨機類支持的合約。

創建該接口的實現,其中一個應該使用原始的Random類,另一個將是您的“隨機”實現。

在需要的時間和地點提供正確的實施。

答案A ,有點令人費解,但它確實有效

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 ,意圖imho更清楚一點。 定義一個返回函數的公共接口。 然后只需選擇要使用的功能。

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();
       ...
   }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM