Заставьте объект динамично реализовать интерфейс в коде

Я хочу сделать эту тестовую передачу - кто-либо понял, как сделать это?

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, и т.д...., но как?

7
задан Jan 16 August 2010 в 21:04
поделиться

5 ответов

Использование Castle DP (очевидно)

Что ж, вам нужно будет создать новый объект для возврата, поскольку вы не можете получить новый интерфейс существующего типа во время выполнения вашей программы.

Для этого вам нужно будет создать прокси, а затем реплицировать состояние вашего ранее существовавшего объекта на прокси. DP не делает этого OOTB. В v2.5 вы могли использовать новый прокси-сервер класса с целью, но это работало бы только в том случае, если бы все свойства типа были виртуальными.

В любом случае. Вы можете заставить новый тип получить интерфейс IWithId либо путем смешивания прокси с существующим объектом, реализующим свойство. Затем вызовы членов интерфейса будут перенаправлены на объект.

В качестве альтернативы вы можете предоставить его в качестве дополнительного интерфейса для реализации и сделать так, чтобы перехватчик выполнял роль разработчика.

3
ответ дан 7 December 2019 в 03:09
поделиться

Почему бы не использовать фиктивный фреймворк , такой как Moq или RhinoMocks. Они позволяют динамически реализовывать интерфейсы без необходимости создавать прокси напрямую.

Используя Moq, вы можете написать:

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

Кроме того, вам придется проделать сложную работу, чтобы получить то, что вы хотите. Насколько мне известно, вы не можете добавить метод или свойства к существующему классу во время выполнения. Вы можете вернуть экземпляр нового объекта, который динамически наследуется от переданного типа. Castle определенно позволяет это, но необходимый код не особенно элегантен или прост.

1
ответ дан 7 December 2019 в 03:09
поделиться

Если оставить в стороне вопрос о том, как динамически присоединить свойство или интерфейс, похоже, что вы пытаетесь расширить существующие классы дополнительными данными. Очень типичное решение этой проблемы - использовать Dictionary и сохранить его в некотором классе обслуживания, поддерживающем отображение. Теперь, когда вам нужен доступ к SomethingExtra, вы просто запрашиваете у класса обслуживания соответствующую информацию.

Преимущества:

  1. Реализацию легче понять и поддерживать, чем решение, использующее отражение и динамическую генерацию прокси.

  2. Если вам нужно унаследовать от расширяемого типа, класс не может быть запечатан. Связывание информации извне отлично работает с запечатанными классами.

  3. Вы можете связать информацию, не неся ответственность за создание дополненного объекта. Вы можете взять экземпляры, созданные где угодно, и связать новую информацию.

Недостатки:

  1. Вам необходимо внедрить экземпляр службы, поддерживающий отображение. Вы можете сделать это через структуру IoC, если вы используете одну, ручную инъекцию (обычно передается через конструктор) или, если нет другой альтернативы, через статический аксессор Singleton.

  2. Если у вас очень большое количество экземпляров и они быстро создаются / удаляются, накладные расходы словаря могут стать заметными. Я подозреваю, что вам понадобится очень большая нагрузка, прежде чем накладные расходы станут заметными по сравнению с реализацией проксирования.

1
ответ дан 7 December 2019 в 03:09
поделиться

Я фактически ответил на аналогичный вопрос на прошлой неделе. Я также написал небольшую библиотеку, которая делает это единственное. Он доступен в моем блоге , который я беззастенчиво подключаю.

То, что вам нужно, почти достижимо с помощью 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!");
}
1
ответ дан 7 December 2019 в 03:09
поделиться

На данный момент я использую 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;
    }
}
3
ответ дан 7 December 2019 в 03:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: