C# 代表与事件

C# 代表与事件,c#,.net,vb.net,events,delegates,C#,.net,Vb.net,Events,Delegates,当一个类不能(或不应该)做某事时,事件或委托可能是一个解决方案 说 在这里,在代理方法中,科学家方法直接由总统课堂使用,如果一位总统提出一个问题,科学家会回答 然而,在.NETFramework代码中,我没有观察到委托的直接使用。直接使用它是错误的吗?如果,为什么?在框架中大量使用委托。LINQ就是一个明显的例子: var result = someCollection.Where(input => input.MatchesSomeCriteria); 其中接受具有特定签名的委托,该签

当一个类不能(或不应该)做某事时,事件或委托可能是一个解决方案

在这里,在代理方法中,科学家方法直接由总统课堂使用,如果一位总统提出一个问题,科学家会回答


然而,在.NETFramework代码中,我没有观察到委托的直接使用。直接使用它是错误的吗?如果,为什么?

在框架中大量使用委托。LINQ就是一个明显的例子:

var result = someCollection.Where(input => input.MatchesSomeCriteria);
其中
接受具有特定签名的委托,该签名被调用以确定是否在结果中包含项。最常用的方法是如上所示的lamba方法,但您也可以传递一个方法:

string[] nums = new[]{ "1", "2", "3"};
int sum = nums.Select(int.Parse).Sum();
int.Parse
与本例中
Select
所需的委托签名(
Func
)相匹配,因此将为
nums
中的每个字符串调用该签名


通常,当委托被直接使用时,它们被作为将使用它们的方法调用的输入。尽管在某些地方它们是使用者状态的一部分(例如,有一些属于委托类型的属性),但它们并不多。

在原始环境中处理委托可能需要一些 样板代码(定义委托、声明必要的成员变量和创建自定义 保留封装的注册/注销方法等)

除了输入时间之外,在raw中使用委托作为应用程序回调的另一个问题 机制是这样一个事实:如果不将类的委托成员变量定义为private,则 调用方将直接访问委托对象。如果是这种情况,调用方将能够 将变量重新分配给新的委托对象(有效地删除要执行的当前函数列表
调用)更糟糕的是,调用方可以直接调用代理的调用列表。

事件

事件实际上是我非常喜欢.net的一个方面,因为它可以让你声明一个更干净的接口。您可以拥有一个总统类,该类宣布它需要一个答案,而无需将其绑定到应答代理的实现,如

interface IPresident
{
     event Action<QuestionArgs, IPresident> HasQuestion;
     void RecieveAnswer(QuestionArgs,Answer);
}
如果一个新的班级想要回答校长的问题,他们所需要做的就是倾听事件发出的信号,有一个问题需要回答,然后在他们能够回答的情况下回答。如果科学家想回答其他人的问题,我们只需要实现一种与他们的事件相关的方法

直接委托调用

上面概述的委托方法的问题是它破坏了封装。它将科学家和总裁的实现紧密结合在一起,使代码变得脆弱。当你有其他人回答问题时会发生什么?在您的示例中,您需要修改Scientist实现以添加新功能,这被称为“脆弱”代码,这是一件坏事。这种技巧在构图中确实有一定的作用,但它很少是最好的选择

linq情况不同,因为您没有将委托公开为类/接口的成员。相反,您使用它作为调用方声明的函子,让您知道调用方感兴趣的信息。因为您正在进行“往返”封装,所以封装保持不变。 这使您可以定义非常干净和强大的API

我们可以以科学家为例,利用这项技术对其进行扩展,让人们发现我们可以回答这样的问题

 partial class Scientist
 {
     public IEnumerable<QuestionArgs> FindQuestions(Predicate<QuestionArgs> interest, IPresident asker)
     {
         return this.Questions.Where( x => interest(x) == true && x.IsAuthorizedToAsk(asker))
     }
 }

 // ...

partial class President
{
    FirePhysicists()
    {
        foreach(var scientist in scientists)
        {
             if(scientist.FindQuestions(x => x.Catagory == QuestionCatagory.Physics, this).Count != 0)
             {
                 scientist.Fire();
             }
         }
      }
 }
部分类科学家
{
公共IEnumerable FindQuestions(谓词兴趣,IPresident询问者)
{
返回此.Questions.Where(x=>interest(x)=true&&x.IsAuthorizedToAsk(asker))
}
}
// ...
半班制校长
{
火物理学家()
{
foreach(科学家中的var科学家)
{
if(科学家.FindQuestions(x=>x.Catagory==QuestionCatagory.Physics,this).Count!=0)
{
科学家。火();
}
}
}
}
请注意,
FindQuestions
方法如何让我们不必实现一堆其他代码来审问科学家,如果没有传递代理的能力,我们将需要这些代码。虽然这不是唯一一种直接调用委托的情况,但它是最常见的委托之一

直接使用它是错误的吗?如果,为什么

不,没有错

我是这样想的。委托字段与事件的关系就像字符串字段与属性的关系一样。也就是说,您可能有:

class Car
{
    private string modelName;
    public string ModelName { get { return this.modelName; } }
    ...
模型名称在逻辑上是汽车的一个属性。当有人问你开什么样的车,你说“一辆福特福克斯”,你是在描述这辆车的特性。你不认为“福特福克斯”是一个“领域”或“字符串”,你认为它是一种汽车的名称。在计算机程序中,字符串字段只是名称存储方式的实现细节。属性可以是字符串,也可以是枚举,或者其他任何内容;要点是,从逻辑上讲,汽车有型号名称,而不是字符串字段

事件和委托的方式相同。汽车可以有一个“爆炸”事件(也许你正在编写一个视频游戏!),爆炸事件由一个委托类型的字段实现。爆炸是汽车的逻辑行为;委托字段是实现事件的机制

那么,直接使用代理是否“错误”?不,当然不是。只不过直接使用字符串是“错误的”。有时需要操纵非属性的字符串,有时需要操纵非事件的委托

诀窍是编写清楚地将机械过程与机械过程分开的代码
 partial class Scientist
 {
     public IEnumerable<QuestionArgs> FindQuestions(Predicate<QuestionArgs> interest, IPresident asker)
     {
         return this.Questions.Where( x => interest(x) == true && x.IsAuthorizedToAsk(asker))
     }
 }

 // ...

partial class President
{
    FirePhysicists()
    {
        foreach(var scientist in scientists)
        {
             if(scientist.FindQuestions(x => x.Catagory == QuestionCatagory.Physics, this).Count != 0)
             {
                 scientist.Fire();
             }
         }
      }
 }
class Car
{
    private string modelName;
    public string ModelName { get { return this.modelName; } }
    ...