Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/274.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将事件视为对象_C#_.net_Asp.net_Events_Oop - Fatal编程技术网

C# 将事件视为对象

C# 将事件视为对象,c#,.net,asp.net,events,oop,C#,.net,Asp.net,Events,Oop,C#还不够吗?这里我举了一个(可能是坏的)例子 public class Program { public event EventHandler OnStart; public static EventHandler LogOnStart = (s, e) => Console.WriteLine("starts"); public class MyCSharpProgram { public string Name { get; set; }

C#还不够吗?这里我举了一个(可能是坏的)例子

public class Program
{
   public event EventHandler OnStart;
   public static EventHandler LogOnStart = (s, e) => Console.WriteLine("starts");

   public class MyCSharpProgram
   {
      public string Name { get; set; }
      public event EventHandler OnStart;
      public void Start()
      {
          OnStart(this, EventArgs.Empty);
      }
   }

   static void Main(string[] args)
   {
      MyCSharpProgram cs = new MyCSharpProgram { Name = "C# test" };
      cs.OnStart += LogOnStart;  //can compile
      //RegisterLogger(cs.OnStart);   // Line of trouble
      cs.Start();   // it prints "start" (of course it will :D)

      Program p = new Program();
      RegisterLogger(p.OnStart);   //can compile
      p.OnStart(p, EventArgs.Empty); //can compile, but NullReference at runtime
      Console.Read();
    }

    static void RegisterLogger(EventHandler ev)
    {
      ev += LogOnStart;
    }
}

RegisterLogger(cs.OnStart)导致编译错误,因为“事件XXX只能出现在+=或-=blabla的左侧”。但为什么RegisterLogger(p.OnStart)可以?同时,虽然我注册了p.OnStart,但它也会抛出一个NullReferenceException,似乎p.OnStart并没有“真正”传递给一个方法。

对RegisterLogger进行以下更改,声明
ev
作为事件处理程序的引用参数

static void RegisterLogger(ref EventHandler ev) 
{ 
  ev += LogOnStart; 
}
然后,您的调用点在调用方法时还需要使用'ref'关键字,如下所示

RegisterLogger(ref p.OnStart);

对RegisterLogger进行以下更改,将
ev
声明为事件处理程序的引用参数

static void RegisterLogger(ref EventHandler ev) 
{ 
  ev += LogOnStart; 
}
然后,您的调用点在调用方法时还需要使用'ref'关键字,如下所示

RegisterLogger(ref p.OnStart);

无法编译的原因:

寄存器记录器(cs.OnStart)

。。。事件处理程序和将其传递给的方法位于不同的类中。C#非常严格地处理事件,只允许事件出现在其中的类执行除添加处理程序以外的任何操作(包括将其传递给函数或调用它)

例如,它也不会编译(因为它位于不同的类中):

至于不能以这种方式将事件处理程序传递给函数,我不确定。我猜事件就像值类型一样运行。通过ref传递它将解决您的问题,不过:

static void RegisterLogger(ref EventHandler ev)
{
    ev += LogOnStart;
}

无法编译的原因:

寄存器记录器(cs.OnStart)

。。。事件处理程序和将其传递给的方法位于不同的类中。C#非常严格地处理事件,只允许事件出现在其中的类执行除添加处理程序以外的任何操作(包括将其传递给函数或调用它)

例如,它也不会编译(因为它位于不同的类中):

至于不能以这种方式将事件处理程序传递给函数,我不确定。我猜事件就像值类型一样运行。通过ref传递它将解决您的问题,不过:

static void RegisterLogger(ref EventHandler ev)
{
    ev += LogOnStart;
}

当一个对象声明一个事件时,它只向类外的事件公开添加和/或删除处理程序的方法(前提是它不重新定义添加/删除操作)。在它内部,它被视为一个“对象”,工作起来或多或少像一个声明的委托变量。如果没有向事件添加任何处理程序,就好像它从未初始化过一样,并且
null
。这是故意的。以下是框架中使用的典型模式:

public class MyCSharpProgram
{
    // ...

    // define the event
    public event EventHandler SomeEvent;

    // add a mechanism to "raise" the event
    protected virtual void OnSomeEvent()
    {
        // SomeEvent is a "variable" to a EventHandler
        if (SomeEvent != null)
            SomeEvent(this, EventArgs.Empty);
    }
}

// etc...
现在,如果您必须坚持将委托公开在类之外,请不要将其定义为事件。然后可以将其视为任何其他字段或属性

我修改了您的示例代码以说明:

public class Program
{
    public EventHandler OnStart;
    public static EventHandler LogOnStart = (s, e) => Console.WriteLine("starts");

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

        // just a field to an EventHandler
        public EventHandler OnStart = (s, e) => { /* do nothing */ }; // needs to be initialized to use "+=", "-=" or suppress null-checks
        public void Start()
        {
            // always check if non-null
            if (OnStart != null)
                OnStart(this, EventArgs.Empty);
        }
    }

    static void Main(string[] args)
    {
        MyCSharpProgram cs = new MyCSharpProgram { Name = "C# test" };
        cs.OnStart += LogOnStart;  //can compile
        RegisterLogger(cs.OnStart);   // should work now
        cs.Start();   // it prints "start" (of course it will :D)

        Program p = new Program();
        RegisterLogger(p.OnStart);   //can compile
        p.OnStart(p, EventArgs.Empty); //can compile, but NullReference at runtime
        Console.Read();
    }

    static void RegisterLogger(EventHandler ev)
    {
        // Program.OnStart not initialized so ev is null
        if (ev != null) //null-check just in case
            ev += LogOnStart;
    }
}

当一个对象声明一个事件时,它只向类外的事件公开添加和/或删除处理程序的方法(前提是它不重新定义添加/删除操作)。在它内部,它被视为一个“对象”,工作起来或多或少像一个声明的委托变量。如果没有向事件添加任何处理程序,就好像它从未初始化过一样,并且
null
。这是故意的。以下是框架中使用的典型模式:

public class MyCSharpProgram
{
    // ...

    // define the event
    public event EventHandler SomeEvent;

    // add a mechanism to "raise" the event
    protected virtual void OnSomeEvent()
    {
        // SomeEvent is a "variable" to a EventHandler
        if (SomeEvent != null)
            SomeEvent(this, EventArgs.Empty);
    }
}

// etc...
现在,如果您必须坚持将委托公开在类之外,请不要将其定义为事件。然后可以将其视为任何其他字段或属性

我修改了您的示例代码以说明:

public class Program
{
    public EventHandler OnStart;
    public static EventHandler LogOnStart = (s, e) => Console.WriteLine("starts");

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

        // just a field to an EventHandler
        public EventHandler OnStart = (s, e) => { /* do nothing */ }; // needs to be initialized to use "+=", "-=" or suppress null-checks
        public void Start()
        {
            // always check if non-null
            if (OnStart != null)
                OnStart(this, EventArgs.Empty);
        }
    }

    static void Main(string[] args)
    {
        MyCSharpProgram cs = new MyCSharpProgram { Name = "C# test" };
        cs.OnStart += LogOnStart;  //can compile
        RegisterLogger(cs.OnStart);   // should work now
        cs.Start();   // it prints "start" (of course it will :D)

        Program p = new Program();
        RegisterLogger(p.OnStart);   //can compile
        p.OnStart(p, EventArgs.Empty); //can compile, but NullReference at runtime
        Console.Read();
    }

    static void RegisterLogger(EventHandler ev)
    {
        // Program.OnStart not initialized so ev is null
        if (ev != null) //null-check just in case
            ev += LogOnStart;
    }
}
“事件XXX只能出现在+=或-=blabla的左侧”

这实际上是因为C#“OO够了。”OOP的核心原则之一是封装;事件提供了这样一种形式,就像属性一样:在声明类内部,可以直接访问它们,但在外部,它们只向
+=
-=
操作符公开。这使得声明类完全控制调用事件的时间。客户机代码只能对调用它们时发生的事情有发言权

代码
RegisterLogger(p.OnStart)
编译的原因是它是在
Program
类的范围内声明的,在该类中声明了
Program.OnStart
事件

代码
RegisterLogger(cs.OnStart)
没有编译的原因是它是在
程序
类的范围内声明的,但是
MyCSharpProgram.OnStart
事件(显然)是在
MyCSharpProgram
类内声明的

正如所指出的,在
p.OnStart(p,EventArgs.Empty)行上获得
NullReferenceException
的原因
是指调用
RegisterLogger
时为局部变量指定一个新值,而不影响作为参数传入时该局部变量所指定的对象。为了更好地理解这一点,请考虑下面的代码:

static void IncrementValue(int value)
{
    value += 1;
}

int i = 0;
IncrementValue(i);

// Prints '0', because IncrementValue had no effect on i --
// a new value was assigned to the COPY of i that was passed in
Console.WriteLine(i);
正如将
int
作为参数并为其分配新值的方法只会影响复制到其堆栈中的局部变量一样,将
EventHandler
作为参数并为其分配新值的方法也只会影响其局部变量(在分配中)

“事件XXX只能出现在+=或-=blabla的左侧”

这实际上是因为C#“OO够了。”OOP的核心原则之一是封装;事件提供了这样一种形式,就像属性一样:在声明类内部,可以直接访问它们,但在外部,它们只向
+=
-=
操作符公开。这使得声明类完全控制调用事件的时间。客户机代码只能对调用它们时发生的事情有发言权

代码
RegisterLogger(p.OnStart)
编译的原因是它是在