简体   繁体   English

用C#替换类实现

[英]Replacing a class implementation in C#

Can I replace (or override) a class definition with one of my own implementation? 我可以用自己的实现替换(或覆盖)类定义吗?

Explanation: 说明:

  • I have a class with a System.Random instance, and I am trying to test it without modifying its original code... 我有一个带有System.Random实例的类,我试图在不修改原始代码的情况下测试它...
  • So I am planning to replace Random class with one of my own implementation, that allows controlling generated sequence.. Please see this question . 所以我打算用我自己的一个实现替换Random类,它允许控制生成的序列..请看这个问题

` `

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

` `

What are the possible ways that I can replace implementation of Random class with another one without changing (or with minimum changes) the original code file?? 有什么方法可以用另一个替换Random类的实现而不改变(或用最小的改动)原始代码文件?

Edit: 编辑:
One solution is to inherit Random class and modify it... but that requires changing each Random instance with the inherited one... I do not want to modify the code, because I am just testing it!! 一种解决方案是继承Random类并对其进行修改......但这需要使用继承的实例更改每个Random实例...我不想修改代码,因为我只是测试它!
Edit 2: 编辑2:
Can I say in C#: "Each Random class in this file is MyNamespace.Random not System.Random" ?? 我可以在C#中说:“这个文件中的每个Random类都是MyNamespace.Random而不是System.Random”?

EDIT: As ChaosPandion points out, to test the logic in the method it would be better to take a double parameter ie double bar(double input) { ... } , but if you have a reason to need to inject your own source of randoms, you could use one of these approaches: 编辑:正如ChaosPandion所指出的,为了测试方法中的逻辑,最好采用双参数即double bar(double input) { ... } ,但如果你有理由需要注入自己的源randoms,您可以使用以下方法之一:

You could modify bar to take a Func<double> to obtain values from: 您可以修改bar以获取Func<double>以从以下位置获取值:

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

Then you can inject your own implementation for testing, and in the main program use: 然后你可以注入自己的实现进行测试,并在主程序中使用:

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

Alternatively you could create your own IRandom interface and then implement a wrapper around the real Random class and use mocks for testing: 或者,您可以创建自己的IRandom接口,然后在真正的Random类周围实现一个包装器,并使用模拟进行测试:

interface IRandom
{
    double Next();
}

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

You can do this and no code changes to the source are necessary. 您可以执行此操作,并且不需要对源进行代码更改。

You can use reflection to inject a mock or derived instance. 您可以使用反射来注入模拟或派生实例。

Since System.Random is not sealed , your best bet here is to derive a new class, and then you only need to override the desired methods. 由于System.Random没有sealed ,这里最好的选择是派生一个新类,然后你只需要覆盖所需的方法。

I'm assuming that you are using some type of unit testing framework, so the example below is for that. 我假设你正在使用某种类型的单元测试框架,所以下面的例子是为了这个。

In your unit test file, do this: 在您的单元测试文件中,执行以下操作:

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

I believe that you can replace the implementation of a class like Random using tools such as JustMock or TypeMock . 我相信你可以使用JustMockTypeMock等工具替换像Random这样的类的实现。

However, without restructuring your original code to use dependency injection (or otherwise insert a layer of abstraction) you won't be able to do what you're asking using plain C#. 但是,如果不重构原始代码以使用依赖注入(或以其他方式插入抽象层),您将无法使用普通C#执行您所要求的操作。 That said, restructuring your code to get rid of hard dependencies makes the code cleaner and simpler, so I would say that it should be the preferred route unless you're really unable to perform the necessary restructuring. 也就是说,重构代码以摆脱硬依赖会使代码更清晰,更简单,所以我会说它应该是首选路由,除非你真的无法进行必要的重组。

You could derive a class from System.Random and override its methods. 您可以从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 () {...}
}

Use this class to instantiate from Main. 使用此类从Main实例化。

Random random = new PseudoRandom ();

If you need to determine later on whether you are using the system class or your own derived version, you can check that using the "is" operator. 如果以后需要确定是使用系统类还是自己的派生版本,可以使用“is”运算符进行检查。 (Probably not the best design, but it's quick and easy) (可能不是最好的设计,但它快速而简单)

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

Take a look into using Dependency Injection . 看一下使用依赖注入 It is designed for exactly this circumstance. 它专为这种情况而设计。 There are several different DI/IoC frameworks for c#. c#有几种不同的DI / IoC框架。

Unity and Ninject are both good but there are many others. Unity和Ninject都很好,但还有很多其他的。

You need to add a layer of abstraction. 您需要添加一个抽象层。

Create an interface that defines the contract you need your Random classes to support. 创建一个接口,定义您需要随机类支持的合约。

Create implementations of that interface, one of which should utilize the original Random class, the other would be your "random" implementation. 创建该接口的实现,其中一个应该使用原始的Random类,另一个将是您的“随机”实现。

Provide the correct implementation when and where needed. 在需要的时间和地点提供正确的实施。

Answer A , a bit convoluted but it works 答案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();
       ...
   }
}

Answer B , a bit clearer on intent imho . 回答B ,意图imho更清楚一点。 Define a common interface which returns a function. 定义一个返回函数的公共接口。 Then just choose which function to use. 然后只需选择要使用的功能。

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