用C#动态实现接口的最佳方式是什么

本文关键字:最佳 方式 是什么 接口 动态 实现 | 更新日期: 2023-09-27 18:26:52

我经常发现,仅仅因为某个方法调用需要一次接口,就必须实现一个接口,这会分散我的注意力。我必须在其他地方创建一个类,实现接口等。

Java有一个名为"匿名类"的特性,它允许实现"内联"接口。因此,我的问题是:使用现有语法在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<...>的粉丝,但我离不开它。

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

有"更好"的方法吗?

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

用C#动态实现接口的最佳方式是什么

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

例如,以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
}

看看"即兴界面"(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"));
    }
}

在C#中实现所需功能的一个好方法是使用Clay对象:

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覆盖强制转换运算符,并为接口创建一个动态代理(使用Castle),将成员委托给Clay对象。

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

如果你有这样的界面:

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);

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

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的技巧是完全不需要的,C#是一种更好的语言。

可以执行更干净的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
}

不幸的是,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);

或者也可以使用更明显的命名参数,特别是如果您的接口有不止一个方法:

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

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

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

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