繁体   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