19

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

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# 的火焰大战。

4

7 回答 7

7

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

例如,以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
}
于 2013-08-08T20:00:36.990 回答
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"));
    }
}
于 2013-08-08T20:16:08.103 回答
5

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

如果你有这样的界面:

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

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

于 2015-07-06T21:00:19.757 回答
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,这是一种更好的语言。

于 2013-08-08T19:26:34.363 回答
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
}
于 2013-08-08T23:09:11.540 回答
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);

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

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

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

于 2016-07-20T14:03:17.063 回答
1

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

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

于 2017-11-13T23:03:46.180 回答