【问题标题】:What is the nicest way to dynamically implement an interface in C#?在 C# 中动态实现接口的最佳方法是什么?
【发布时间】:2013-08-10 15:39:45
【问题描述】:

我经常发现仅仅因为某个方法调用需要它一次就必须实现一个接口会让人分心。我必须在其他地方创建一个类,实现接口等。

Java 有一个名为Anonymous Classes 的特性,它允许实现“内联”接口。因此,我的问题是:您能想到的使用现有语法在 C# 中完成类似操作的 最佳 方式是什么(我意识到“最佳”是主观的)。我正在寻找好的语法,不一定是性能。

我在 C# 中将以下内容作为 POC 实现:

给定

interface IFoobar
{
   Boolean Foobar(String s);
}

IFoobar foo = Implement.Interface<IFoobar>(new {
   Foobar = new Func<String, Boolean>(s => s == "foobar")
});

这使用匿名对象和一些反射/发射来实现IFoobar 接口(忽略属性、泛型方法和重载)。但是,我不是 new Func&lt;...&gt; 的粉丝,但我不能没有。

环顾四周,我注意到一个名为Impromptu Interface 的库,但它支持方法的语法并没有给我留下深刻印象。

有没有“更好”的方式?

编辑:我不是在寻找 Java 与 C# 的火焰大战。

【问题讨论】:

  • I often find it quite a distraction to have to implement an interface just because I need it once for some method call. - 使用Delegates。这就是 C# 与 java 的区别。
  • 你发现自己经常这样做吗?
  • 不,时不时地,在编写单元测试时可能更是如此。我意识到这不一定是好的做法,但我主要是好奇。
  • “我的问题不在于 Java 的匿名类与 C# 委托。”我的答案也不是。但如果你不想听,那我也没关系。
  • 嗯,ImpromptuInterface 有你确切的 POC 语法,只需将 Implement.Interface 换成 Impromptu.ActLike 以便 IFoobar foo = Impromptu.ActLike&lt;IFoobar&gt;(new { Foobar = new Func&lt;String, Boolean&gt;(s =&gt; s == "foobar") });

标签: c# dynamic interface


【解决方案1】:
public class Foo
{
   public Func<string,bool> TheDelegate {get;set;}
}

public class Bar
{
   public bool Implementation(string s)
   {
      return s == "True";
   }
}

public class Usage
{
    var myBar = new Bar();
    var myFoo = new Foo { TheDelegate = myBar.Implementation };

    //Or

    var myFoo = new Foo { TheDelegate = x => x == "True" };   
    //This removes the need for Bar completely
}

正如您在上面的示例中看到的,在 C# 中完全不需要类似 java 的 hack,这是一种更好的语言。

【讨论】:

  • 此示例未实现原始问题中的IFoobar 接口。您如何将实现传递给采用IFoobar 实例的方法?
  • @user10789 C# 中不需要这样的接口,因为有委托。从代码中完全删除该接口,并在需要定义此类“可互换方法”时放置一个 Delegate 声明。
  • @Marcus 说,“实现一个接口只是因为我需要它一次用于某些方法调用”......“在编写单元测试时更是如此”。我假设他想将一个实现传递给一个方法,然后调用使用一个或多个接口的方法。代表不适用于这种情况或您无法更改所调用代码的任何其他情况。
【解决方案2】:

您提到您不需要经常这样做,不关心性能,并且通常希望在单元测试期间这样做。为什么不使用模拟框架?

Moq库为例:

public interface IFoobar {
   Boolean Foobar(String s);
}  

void Main() {
    var foo = new Mock<IFoobar>();
    foo.Setup(x => x.Foobar(It.IsAny<string>()))
       .Returns((string s) => s == "foobar");

    foo.Object.Foobar("notbar"); // false
    foo.Object.Foobar("foobar"); // true
}

【讨论】:

    【解决方案3】:

    看看“即兴界面”(https://github.com/ekonbenefits/impromptu-interface)。

    它将允许您执行类似...

    class Program
    {
        static void Main(string[] args)
        {
            Bar b = new Bar();
            b.DoSomethingWithFoo(new
            {
                Foobar = Return<string>.Arguments<string>(r => "foo")
            }.ActLike<IFoo>());
        }
    }
    
    public interface IFoo
    {
        string Foobar(String s);
    }
    
    public class Bar
    {
        public void DoSomethingWithFoo(IFoo foo)
        {
            Console.WriteLine(foo.Foobar("Hello World"));
        }
    }
    

    【讨论】:

      【解决方案4】:

      可以使用更简洁的 lambda 语法,但代价是 Create() 内部的静态类型检查。

      我能够使用 ImpromptuInterface 来做到这一点:

      IFoobar foo = Implement.Interface(new {
          Foobar = Function.Create(s => s == "foobar"),
      });
      

      通过创建以下类:

      public static class Implement{
      
          public static dynamic Interface(object source){
              return Impromptu.ActLike(source);
          }
      
      }
      
      public static class Function{
      
          public static Func<dynamic> Create(Func<dynamic> del){
              return del;
          }
      
          public static Func<dynamic,dynamic> Create(Func<dynamic,dynamic> del){
              return del;
          }
          public static Func<dynamic,dynamic,dynamic> Create(Func<dynamic,dynamic, dynamic> del){
              return del;
          }
      
          public static Func<dynamic,dynamic,dynamic,dynamic> Create(Func<dynamic,dynamic, dynamic,dynamic> del){
              return del;
          }
          //...Add more if you want
      }
      

      【讨论】:

        【解决方案5】:

        使用Clay objects

        public interface IFoobar{
          Func<string, bool> Foobar { get; set; }  
        }
        

        使用该界面,您可以执行以下操作:

        dynamic New = new ClayFactory();
        
        var foobar= New.FooBar();
        foobar.Foobar = new Func<string, bool>(s => s == "foobar");
        
        // Concrete interface implementation gets magically created!
        IFoobar lou = foobar;
        var result =lou.Foobar("foo");// return false
        

        使魔法成为可能的是Clay 重写了cast 操作符 并为接口创建了一个动态代理(使用Castle)将成员委托给Clay 对象。 p>

        另一种方法是使用Impromptu Interface 库,它允许您使用接口包装任何对象。这意味着现在任何对象都可以具有动态行为。如果对象具有接口方法,您可以根据需要直接将行为附加到它们。如果对象没有接口方法,则定义一个接口并将对象包装在其中,然后根据需要将行为附加到接口方法。此库是应用Object Adapter pattern 的自动方式。

        如果你有这样的界面:

        public Interface IFoobar
        {
           bool Foobar(string s);
        }
        

        你可以装饰一个匿名类型,如下所示:

        //Anonymous Class
        var anon = new {Foobar= Return<bool>.Arguments<string>(s => s == "foobar")};
        
        var myInterface = anon.ActLike<IFoobar>();
        

        或者你也可以使用ExpandoObject

        dynamic expando = Build<ExpandoObject>.NewObject(Foobar: Return<bool>.Arguments<string>(s => s == "foobar"));
        
        IMyInterface myInterface = Impromptu.ActLike(expando);
        

        如果您想实现多个接口,请查看我在此post 中的回答。

        【讨论】:

          【解决方案6】:

          不幸的是,C# 中的匿名类无法像 Java 中那样实现接口。但是,您可以创建某种适配器类,而无需对外部项目有任何额外的依赖。只需创建一个使用Func 实现您的接口的基类:

          interface IFoo
          {
              bool DoSomething(string value);
          }
          class Bar : IFoo
          {
              private readonly Func<string, bool> m_DoSomething;
              public Bar(Func<string, bool> DoSomething) { this.m_DoSomething = DoSomething; }
          
              public bool DoSomething(string value)
              { 
                  return this.m_DoSomething(value);
              } 
          }
          

          现在你可以这样称呼它:

          var result = new Bar(x => true);
          

          或者也使用更明显的named arguments,特别是如果你的界面有不止一种方法:

          var result = new Bar(DoSomething: x => true);
          

          唯一的缺点是你需要为你拥有的每个接口一个实现类。因此,这种方法仅在您想以不同的行为多次实现每个接口时才有用。所以每当我需要对同一个接口进行不同的实现时,我都会使用这种方法。

          【讨论】:

            【解决方案7】:

            如果您最大的抱怨是在其他地方实现接口,为什么不在您的方法之前/之后直接创建一个嵌套类呢? (与 Java 静态嵌套类相比。)

            这比创建/使用一些动态框架更符合 C# 的习惯。

            【讨论】:

              猜你喜欢
              • 2011-06-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-05-24
              • 1970-01-01
              • 2011-01-04
              • 2010-09-12
              • 1970-01-01
              相关资源
              最近更新 更多