在C#中动态实现接口的最好方法是什么?

Mar*_*cus 16 c# interface dynamic

我经常觉得必须实现一个接口是非常分心的,因为我需要一次方法调用.我必须在其他地方创建一个类,实现接口等.

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")
});
Run Code Online (Sandbox Code Playgroud)

这使用匿名对象和一些反射/发射来实现IFoobar接口(忽略属性,泛型方法和重载).但是,我不是这些new Func<...>东西的粉丝,但离不开它们.

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

有一种"更好"的方式吗?

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

小智 8

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

例如,以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
}
Run Code Online (Sandbox Code Playgroud)


iam*_*lin 6

看一下"即兴界面"(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"));
    }
}
Run Code Online (Sandbox Code Playgroud)


oct*_*ccl 5

在 C# 中做你需要的一个好方法可能是使用Clay 对象

public interface IFoobar{
  Func<string, bool> Foobar { get; set; }  
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

使魔术成为可能的原因Clay是覆盖强制转换运算符并为接口创建动态代理(使用Castle),将成员委托给 Clay 对象。

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

如果你有这样的界面:

public Interface IFoobar
{
   bool Foobar(string s);
}
Run Code Online (Sandbox Code Playgroud)

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

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

var myInterface = anon.ActLike<IFoobar>();
Run Code Online (Sandbox Code Playgroud)

或者你也可以使用ExpandoObject

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

IMyInterface myInterface = Impromptu.ActLike(expando);
Run Code Online (Sandbox Code Playgroud)

如果您想实现多个接口,请查看我在这篇文章中的回答。


Fed*_*gui 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
}
Run Code Online (Sandbox Code Playgroud)

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

  • @Marcus 说,“实现一个接口只是因为我需要它一次来进行某些方法调用”......“在编写单元测试时更是如此”。我假设他想要将实现传递给一个方法,然后该方法将调用使用一个或多个接口的方法。对于这种情况或任何其他您无法更改正在调用的代码的情况,委托将不起作用。 (3认同)
  • 此示例未实现原始问题中的“IFoobar”接口。如何将实现传递给采用“IFoobar”实例的方法? (2认同)