【问题标题】:Use Expression tree to implement interface by proxying existing implementation使用表达式树通过代理现有实现来实现接口
【发布时间】:2015-05-16 19:29:57
【问题描述】:

假设我有以下接口和两个实现。

public interface IPerson
{
    string Talk();
    void Lunch();

}

public class DutchPerson : IPerson
{
    public string Talk()
    {
        return "Ik spreek Nederlands.";
    }

    public void Lunch()
    {
        EatBroodjeKroket();
    }

    private void EatBroodjeKroket()
    {

    }
}

public class EnglishPerson : IPerson
{
    public string Talk()
    {
        return "I speak English.";
    }

    public void Lunch()
    {
        EatFishAndChips();
    }

    private void EatFishAndChips()
    {

    }
}




public class ImplementationBuilder<T>
{

    private Dictionary<Type, IPerson> _instances;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="instances">Instances that will be used in proxy</param>
    public ImplementationBuilder(Dictionary<Type, IPerson> instances)
    {
        _instances = instances;
    }

    public void Setup()
    {

    }

    /// <summary>
    /// this should return the generated instance
    /// </summary>
    public IPerson GetProxy()
    {


        return null;
    }
}

我想做的是使用表达式树创建一个新的实现,并混合和匹配来自两个(或更多)实现的方法。基本上我想创建一个实现 IPerson 的代理。所以我传入将要使用的实例,我想使用 Setup 方法来“配置”代理。这基本上是一个列表或字典,每个项目都应该是方法和类型。在使用表达式树生成代理时,应该检查要使用的实现。

所以

谈话,DutchPerson 午餐,英语人

GetProxy 方法将返回(伪代码)

public class MergedInstance : IPerson
{
    public void Talk() {

       return DutchPerson.Talk()
    }

    public Lunch() {

       EnglishPerson.Lunch()
    }
}

我主要想要这个,因为代理实现包含很多方法,我希望能够使用功能标志在实现之间切换。

所以我是不是以错误的方式看待它,使用表达式树这甚至是合理可行的。我正在使用 .NET 4.5.1。

【问题讨论】:

    标签: c# expression-trees


    【解决方案1】:

    通过System.Reflection.Emit + Expression 树木进行操作非常/非常复杂...但是如果您有一个不变的IPerson,您可以:

    public class MergedInstance : IPerson
    {
        public IPerson TalkIPerson { get; set; }
        public IPerson LunchIPerson { get; set; }
    
        public void Talk() 
        {
           TalkIPerson.Talk();
        }
    
        public Lunch() {
           LunchIPerson.Lunch();
        }
    }
    

    等等

    那么你的GetProxy就可以了

    public IPerson GetProxy()
    {
        var merged = MergedInstance();
    
        merged.TalkIPerson = ...
        merged.LunchIPerson = ...
    
        return merged;
    }
    

    在运行时创建代理类的唯一优点是:

    • 您可以使用在运行时选择的任何接口(因此它可以与IPersonIAnimal、...一起使用)。逻辑可以通过其他方法通过委托注入和/或由 XML 或其他配置对象读取

    • MergedInstance 类中的每个方法的引用可以少于一个(取决于配置,您需要的引用数量在所有方法使用的单个引用和每个方法使用 1 个之间是可变的方法,所以最大值相同,变化的是最小值)

    【讨论】:

      【解决方案2】:

      我不认为表达式树是你的朋友。阅读运行时编织(代理)。一个非常易于理解和使用的框架是 Castle DynamicProxy (http://www.castleproject.org/projects/dynamicproxy/)。阅读一些howtos,你会发现自己很惊讶。

      如果性能真的很关键,我仍然会尝试组合而不是动态生成类,但是您可以尝试 Reflection.Emit 方式(正如@xantos 建议的那样),一旦生成类(这本身并不快)。祝你好运

      【讨论】:

      • 您对表达式树的看法是正确的。所以我采用了动态代理方式。我在检查 Moq 的源代码时遇到了它。困难的部分是“设置”方法。我想要一种强类型的方式来配置从哪个类中使用哪个方法。我现在对它并不完全满意,但是当我对它感到满意时,我会在 Github 上发布它。但 IInterceptor 是我的朋友。
      猜你喜欢
      • 1970-01-01
      • 2017-09-22
      • 2010-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-03
      • 2021-01-18
      • 1970-01-01
      相关资源
      最近更新 更多