2

C# 是一种多范式语言。通过嵌套接口和类,可以获得对象的组合。通过这种方式,可以将某个问题分解为多个简单的问题,从而为每个组件提供自己的难题来解决。同样的技巧可以以稍微不同的方式完成,可以将功能组合在一起,每个功能负责解决自己的小任务,而所有功能结合起来并相互关联,可以解决主要问题。

遵循 SOLID 原则,我发现自己处于这样一种情况,即 95% 的接口只带有一个名为 do-something 的方法,并且接口的名称是 something-doer。实现此类接口的类是 DI'ed 与完成该方法应该做的任何事情所需的一些组件。这种方法基本上是手动关闭一个函数。如果是这样,我为什么不与自然且免费(无需打字)的代表一起去?如果我一直将单个方法接口转换为委托,我将从我的代码中消除 95% 的委托,使其看起来像是用函数式语言编写的。但这似乎是一件正确的事情,除非有一些大胆的理由坚持使用接口。有没有?

更新:

@Fendy,让我争论一下。你说过

“您无法从 BLL 控制委托逻辑。好吧,委托可以在任何地方定义,而不必在调用者中定义。可以完美地做到这一点:

public namespace Bbl
{
    public static class TestableUtils {
        public static Func<A, B> CreateA2B() {
            Func<A, B> a2b = a => new B(a);
            return a2b;
        }
        public static Func<B, C> CreateB2C() {
            Func<B, C> b2c = b => new C(b);
            return b2c;
        }
        public static Func<A, C> CreateA2C(Func<A, B> a2b, Func<B, C> b2c)
        {
            Func<A, C> a2c = a => b2c(a2b(a));
            return a2c;
        }
    }
}

public namespace Caller
{
    using Bbl;
    public class Demo()
    {
        public void RunMe()
        {
            var a = new A();
            var a2b = TestableUtils.CreateA2B();
            var b2c = TestableUtils.CreateB2C();
            var a2c = TestableUtils.CreateA2C(a2b, b2c);
            var c = a2c(a);
        }
    }
}

“一不小心,到处重复代码”,确实如此,偷懒小心,不要重复做同样的工作。类很容易发生同样的问题。所以代表也不例外。

“如果构造函数注入并使用可变实体,它可能导致有状态接口”我很抱歉我没有看到你的例子有问题。我的意思是它完全按照它的程序设计。如果你改变实体,这就是你所期望的,不是吗?只需防止在构造函数之外设置 Name 即可确保安全。如果你这样做,那么你的例子中的情况将是不可能的。

基本上,在您列出的 4 点中,与使用委托相比使用接口/类时遇到更多问题没有任何关系。或者我只是没有得到它。

4

3 回答 3

2

委托几乎是一个方法接口。

闭包正在创建一个新的嵌套类,在幕后,它只是为您做很多相关的样板,以使代码更清晰。

简而言之,创建委托和 lambdas/闭包是为了解决在没有这些特性的语言中使用接口和嵌套类的问题。在这种情况下,您不会滥用这些功能来使用它们。

也就是说,如果您愿意,使用类/接口没有任何问题;它只是更明确一点。它也更强大,如果您所表示的操作比具有单个方法的小类更复杂,它通常很有用,因此它不像委托和 lambdas 完全替换接口。

于 2013-09-27T14:54:33.120 回答
1

答案是,正如在大多数应用程序设计中应用的那样,是“它取决于”。

如您所知,委托是在调用者中定义的。代表着:

  1. 您无法从 BLL 控制委托逻辑。这也是NO UNIT TEST函数的意思
  2. 一不小心,到处重复代码
  3. 在组合根/构造函数注入处注册委托?先读这个
  4. 如果构造函数注入并使用可变实体,则可能导致有状态接口

第 4 点的示例:

下面的例子只展示了构造函数注入的委托与可变实体如何导致有状态的实现。

public class Employee
{
    public string Name { get; set; }
}

public class EvilDeleg
{
    public EvilDeleg(Action<Employee> act)
    {
        this.act = act;
    }
    Action<Employee> act;
    public void DoSomething(Employee emp)
    {
        Console.WriteLine(emp.Name); // resulting in "First"
        emp.Name = " Evil";
        act(emp);
        Console.WriteLine(emp.Name); // resulting in " Evil Second"
    }
}

public class TestStatefulInterface
{
    public void ConsumeEvilDeleg()
    {
        Employee emp = new Employee();

        EvilDeleg deleg = new EvilDeleg(k => k.Name = k.Name + " Second");
        emp.Name = "First";

        deleg.DoSomething(emp);
        Console.WriteLine(emp.Name); // resulting in " Evil Second"
    }   
}

那么,什么时候适合在单一方法接口上使用委托呢?

我的 2 美分:

  1. 该代码没有业务相关的代码。您想控制系统的业务方面,而让它匿名被控制可能会很痛苦
  2. 代码很可能有 UI / DB - 特定的实现。例如从DataTabletoEntityEntityto UI的转换ViewModel
  3. 这很简单,不需要单元测试
  4. 它被注入到方法中,而不是构造函数中,否则不要使用可变结构作为输入,或者只使用非参数委托

另外,Mark Seeman在他的博客中说:

仅在您只需要抽象一个方法时才有效。一旦你的抽象需要第二种方法,你就需要引入一个适当的接口,或者最好是一个抽象基类。

于 2013-10-02T08:32:26.690 回答
1

好的,事实证明答案是BIG FAT NO,一个不能使用委托而不是单一方法接口的原因有两个:

  1. C# 不支持递归匿名函数。

  2. C# 中没有类型别名,因此函数的签名很快变得丑陋且难以管理。

厄运。

于 2013-10-30T04:16:41.487 回答