7

我想让这个测试通过 - 有人知道怎么做吗?

public class Something
{
    public string Name {get; set}
}

public interface IWithId
{
    public Guid Id {get; set}
}

public class IdExtender 
{
    public static Object Extend(object toExtend)
    {
        ...?
    }
}

public class Tests 
{
    [Test]
    public void Should_extend_any_object()
    {
        var thing = new Something { Name = "Hello World!"};
        var extended = IdExtender.Extend(thing);
        Assert.IsTrue(extended is IWithId);
        Assert.IsTrue(extended.Id is Guid);
        Assert.IsTrue(extened.Name == "Hello World!");
    }
}

我想这样的事情可以用城堡动态代理、linfu等来完成……但是怎么做呢?

4

5 回答 5

3

使用 Castle DP(显然)

好吧 - 你将不得不创建一个新的对象来返回,因为你不能让一个现有的类型在你的程序执行时获得一个新的接口。

为此,您需要创建一个代理,然后将预先存在的对象的状态复制到代理上。DP不这样做OOTB。在 v2.5 中,您可以将新的类代理与 target 一起使用,但这只有在类型上的所有属性都是虚拟的情况下才有效。

反正。IWithId您可以通过将代理与实现该属性的现有对象混合来使新类型获得接口。然后对接口成员的调用将被转发到对象。

或者,您可以将其作为附加接口提供来实现,并让拦截器充当实现者的角色。

于 2010-08-17T06:59:16.977 回答
3

现在我要像这样使用 linfu:

public class IdExtender 
{
    public static Object Extend(object toExtend)
    {
        var dyn = new DynamicObject(toExtend);
        dyn.MixWith(new WithId {
                                Id = Guid.New()
                               });
        var extended = dyn.CreateDuck<IWithId>(returnValue.GetType().GetInterfaces());
        return extended;
    }
}
于 2010-08-16T23:15:57.383 回答
1

撇开如何动态附加属性或接口的问题不谈,听起来您正在尝试做的是用附加数据扩充现有类。此问题的一个非常典型的解决方案是使用 aDictionary<Something, SomethingExtra>并将其存储在维护映射的某个服务类中。现在,当您需要访问SomethingExtra 时,您只需向服务类询问相关信息。

优点:

  1. 与使用反射和动态代理生成的解决方案相比,该实现更易于理解和维护。

  2. 如果需要从被扩展的类型派生,类不能被密封。外部关联信息与密封类一起工作得很好。

  3. 您可以关联信息,而不必负责构建增强对象。您可以在任何地方创建实例并关联新信息。

缺点:

  1. 您需要注入维护映射的服务实例。如果您使用的是手动注入(通常通过构造函数传递),则可以通过 IoC 框架执行此操作,如果没有其他选择,则可以通过 Singleton 静态访问器。

  2. 如果您有大量实例并且正在快速创建/删除它们,则字典开销可能会变得很明显。我怀疑与代理实现相比,在开销变得明显之前,您需要一些非常重的负载。

于 2010-08-16T21:42:41.770 回答
1

为什么不使用像 Moq或 RhinoMocks 这样的模拟框架。它们允许您动态实现接口,而无需直接创建代理。

使用起订量,您可以编写:

var mock = new Mock<IWithId>();  // mock the interface
mock.Setup(foo => foo.Name).Returns("Hello World"); // setup a property

除此之外,你必须做一些复杂的工作才能得到你想要的。据我所知,您不能在运行时向现有类添加方法或属性。您可以返回一个从传入类型动态继承的新对象的实例。Castle 绝对允许这样做,但所需的代码并不是特别优雅或简单。

于 2010-08-16T21:06:56.803 回答
1

上周我实际上已经回答了一个类似的问题。我还写了一个小库来做这件事。它可以从我的博客中获得,我无耻地插入。

您所追求的几乎可以使用 Castle Dynamic Proxy 实现。唯一的限制是现有实例必须实现一个接口,并且您感兴趣的所有属性/方法都可以通过该接口获得。

public static TIntf CreateMixinWithTarget<TIntf>(TIntf target, params object[] instances) where TIntf : class{

    ProxyGenerator generator = new ProxyGenerator();
    ProxyGenerationOptions options = new ProxyGenerationOptions();

    instances.ToList().ForEach(obj => options.AddMixinInstance(obj));

    return generator.CreateInterfaceProxyWithTarget <TIntf>(target, options);
}

[Test]
public void Should_extend_any_object()
{
    var thing = new Something { Name = "Hello World!"};
    var extended = CreateMixinWithTarget<ISomething>(thing, new WithId(), new GuidImpl());
    Assert.IsTrue(extended is IWithId);
    Assert.IsTrue(extended.Id is Guid);
    Assert.IsTrue(extened.Name == "Hello World!");
}
于 2010-08-17T00:43:12.333 回答