我可以用自己的实现替换(或覆盖)类定义吗?
说明:
`
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”??
答案 0 :(得分:5)
double bar(double input) { ... }
,但如果你有理由需要注入自己的random来源,那么你可以使用以下方法之一:
您可以修改条形以获取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 :(得分:3)
您可以执行此操作,无需更改源代码。
您可以使用反射来注入模拟或派生实例。
由于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
}
}
答案 2 :(得分:2)
我相信您可以使用JustMock或TypeMock等工具替换Random
类的实现。
但是,如果不重构原始代码以使用依赖注入(或以其他方式插入抽象层),您将无法使用普通C#执行您所要求的操作。也就是说,重构代码以摆脱硬依赖会使代码更清晰,更简单,所以我会说它应该是首选路由,除非你真的无法进行必要的重组。
答案 3 :(得分: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 () {...}
}
使用此类从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
}
答案 4 :(得分:0)
查看使用Dependency Injection。它专为这种情况而设计。 c#有几种不同的DI / IoC框架。
Unity和Ninject都很好,但还有很多其他的。
答案 5 :(得分:0)
您需要添加一个抽象层。
创建一个界面,定义您需要随机类支持的合约。
创建该接口的实现,其中一个应该使用原始的Random类,另一个应该是你的“随机”实现。
在需要的时间和地点提供正确的实施。
答案 6 :(得分:0)
回答 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();
...
}
}