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

时间:2013-08-08 19:17:39

标签: c# dynamic interface

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

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<...>内容的粉丝,但不能没有。

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

有没有“更好”的方式?

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

7 个答案:

答案 0 :(得分: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
}

答案 1 :(得分: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"));
    }
}

答案 2 :(得分:2)

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的黑客,这是一种更好的语言。

答案 3 :(得分:2)

在C#中执行所需操作的好方法可能是使用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覆盖强制转换运算符并为接口创建动态代理(使用Castle)将成员委派给Clay对象

另一种方法是使用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中查看我的答案。

答案 4 :(得分:1)

可以使用更清晰的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 :(得分:1)

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

唯一的缺点是您需要为每个接口提供一个实现类。因此,如果您希望以不同的行为多次实现每个接口,则此方法才有用。因此,每当我需要针对同一接口的不同实现时,我都会使用这种方法。

答案 6 :(得分:0)

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

比创建/使用某些动态framew(基本上是静态嵌套类)更具惯用性C#。