c#中的委托与接口

本文关键字:接口 | 更新日期: 2023-09-27 18:19:07

只要我目前正在尝试深入研究委托的使用和目的,我就想提出这个问题,尽管在类似的公式中可能已经问过这个问题。

我知道委托在c++中用作函数指针。事实上,在c#中,它们主要是作为接口和多态性的替代品。既然我可以创建一个特定类的子类,并为每个类提供适当的方法,那么什么提供额外的委托呢?在使用委托时,是否存在规定使用委托的情况,或者仅仅是代码的可维护性得到了改善?您会推荐它们在接口上的广泛部署吗?

我只讨论委托,我想把它们的角色与事件角色区分开来。

c#中的委托与接口

是的,委托在很多方面类似于单方法接口。然而:

  • CLR内建了对它们的支持
  • 框架支持它们,包括多播能力和异步调用
  • 以方法组转换,lambda表达式,匿名方法的形式提供额外的c#/VB语言支持
  • 它们被强制用于事件(即事件和委托是一种匹配对)
  • 它们意味着你不需要为你想要创建的每个委托实例在单独的类中实现接口。
最后一点也是最重要的一点——考虑一个LINQ表达式:
var query = collection.Where(x => x > 5)
                      .Select(x => x * x);

现在想象一下,如果要表达x > 5x * x的逻辑,你必须为每个表达式编写一个单独的类,并实现一个接口:无用的数量与有用的代码将是荒谬的。当然,语言可以被设计成允许通过单独的类从lambda表达式转换为接口实现,但是这样您仍然会失去简单地编写单独的方法并创建以该方法为目标的委托的好处。你还会失去多重施放能力。

作为类似的思考练习,考虑循环语句,如whilefor。当我们有goto时,我们真的需要它们吗?不。但是有了它们,生活会更好。委托也是如此,属性、事件等也是如此。它们都简化了开发。

最大的实际区别是你可以为来自同一个类的同一个委托提供不同的委托实例,而你不能通过接口做到这一点。

delegate void XYZ(int p);
interface IXyz {
    void doit(int p);
}
class One {
    // All four methods below can be used to implement the XYZ delegate
    void XYZ1(int p) {...}
    void XYZ2(int p) {...}
    void XYZ3(int p) {...}
    void XYZ4(int p) {...}
}
class Two : IXyz {
    public void doit(int p) {
        // Only this method could be used to call an implementation through an interface
    }
}

从何时使用委托而不是接口(MSDN):

委托和接口都使类设计器能够分离类型声明和实现。给定的接口可以由任何类或结构继承和实现。可以为任何类上的方法创建委托,只要该方法符合委托的方法签名。接口引用或委托可以由不知道实现接口或委托方法的类的对象使用。考虑到这些相似之处,什么时候类设计器应该使用委托,什么时候应该使用接口?

在以下情况下使用委托:

  • 使用事件设计模式
  • 最好封装一个静态方法。
  • 调用者不需要访问实现该方法的对象的其他属性、方法或接口。
  • 简单的合成是理想的。
  • 一个类可能需要一个以上的方法实现。

在以下情况下使用接口:

  • 可以调用一组相关的方法。
  • 一个类只需要一个方法实现。
  • 使用该接口的类将希望将该接口强制转换为其他接口或类类型。
  • 被实现的方法与类的类型或标识相关联:例如,比较方法。

使用单方法接口代替委托的一个很好的例子是IComparable或通用版本IComparable (of T)。IComparable声明了CompareTo方法,该方法返回一个整数,该整数指定了相同类型的两个对象之间的小于,等于或大于关系。IComparable可以用作排序算法的基础。虽然使用委托比较方法作为排序算法的基础是有效的,但它并不理想。因为比较的能力属于类,而且比较算法在运行时不会改变,所以单方法接口是理想的。

来自c#中的委托与接口:

委托和接口在c#中是两个不同的概念,但它们有一个共同点。委托和接口都只包含声明。实现由不同的编程对象完成。

委托和接口是c#中两个不同的概念。

接口允许扩展一些对象的功能,它是接口和实现它的对象之间的契约,而委托只是安全的回调,它们是一种函数指针。

委托相对于接口的唯一真正优势是

  1. 委托甚至可以在。net支持泛型之前处理不同的参数类型。
  2. 类可以创建委托,公开共享相同签名的多个不同方法;用接口代替委托意味着每个类都可以公开一个方法来实现每个委托风格的接口,而无需创建辅助类,但是对于每个额外的实现都需要一个辅助类。

当。net不支持泛型时,委托是必不可少的一部分,因为为每个想要传递的不同函数签名声明不同的非泛型接口是不可行的。如果。net从一开始就支持泛型,除了涉及反射的某些场景外,委托就没有必要了,即使在这种情况下,将Action<T,U>类型作为IAction<T,U>的实现可能也是最有用的(这样,只需要Invoke就可以使用接口的代码)。在类需要创建暴露多个方法的委托的情况下,基于接口的方法将需要创建单方法类,但是在许多需要暴露给定签名的方法数量恰好为一个的常见情况下,将消除创建单独委托实例的需要。

顺便提一下,用接口代替委托并不会阻止创建通用的Combine方法。实际上,接口协方差可以使这种方法在许多方面比现有的Delegate.Combine工作得更好。实现类似于Delegate.Remove的方法将是最笨拙和令人讨厌的,但我认为除了事件订阅管理之外,没有任何情况需要使用Delegate.Remove,并且事件订阅最好使用其他方法来处理。

很晚才接电话。我想强调另一个可以通过委托实现的很酷的事情,我不确定这个功能以前是否可用。

一个委托可以指向多个函数,一个调用就可以调用所有的函数。

public delegate void Log(string data);
private void LogInFile(string data)
{
    Console.WriteLine("Logging in file. Data:" + data);
}
private void LogInConsole(string data)
{
    Console.WriteLine("Logging in console. Data:" + data);
}
private void LogInMemory(string data)
{
    Console.WriteLine("Logging in memory. Data:" + data);
}
[Test]
public void TestDelegate()
{
    Log delegateLogger = LogInFile;
    delegateLogger += LogInConsole;
    delegateLogger += LogInMemory;
    delegateLogger("test");
}

Standard Output: 
Logging in file. Data:test
Logging in console. Data:test
Logging in memory. Data:test