C#事件的实现

Adi*_*oho 4 c# events class

我正在学习关于类的C#事件实现.

我有例子:

Car class继承了Sport和City课程.Car class具有由Sport和City类继承的基本方法调用OnBuy.在OnBuy方法中,事件处理程序已分配给Event Buy.

还有一个名为LicenseService的服务或类,每次购买时都会生成许可证号.

在这种情况下,我实现了事件驱动编程.这是我的git示例:

https://github.com/adityosnrost/CSharpLearningEvent

问题:

  1. 这是在C#上使用Event的正确方法吗?

  2. 如果这是正确的.我可以将OnBuy方法覆盖到每个孩子身上吗?如果覆盖可用,我该怎么办?

  3. 我可以做些什么来使这个样本更好?

谢谢

class Program
{
    static void Main(string[] args)
    {
        Car car = new Car();
        Sport sport = new Sport();
        City city = new City();

        //car.Name();
        //sport.Name();
        //city.Name();

        //Console.ReadLine();

        LicenseService ls = new LicenseService();

        city.Buy += ls.GenerateLicense;

        city.OnBuy();

        Console.ReadLine();
    }
}

internal class Car
{
    internal virtual void Name()
    {
        Console.WriteLine("Car");
    }

    internal event EventHandler Buy;

    internal virtual void OnBuy()
    {
        EventHandler handler = Buy;
        if (null != handler)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

internal class Sport: Car
{
    internal override void Name()
    {
        Console.WriteLine("Sport");
    }
}

internal class City: Car
{
    internal override void Name()
    {
        Console.WriteLine("City");
    }
}

internal class LicenseService
{
    internal void GenerateLicense(object sender, EventArgs args)
    {
        Random rnd = new Random();

        string carType = sender.GetType().ToString();

        string licenseNumber = "";

        for(int i = 0; i < 5; i++)
        {
            licenseNumber += rnd.Next(0, 9).ToString();
        }

        Console.WriteLine("{1} Car has been bought, this is the license number: {0}", licenseNumber, carType);
    } 
}
Run Code Online (Sandbox Code Playgroud)

Ami*_*mit 5

如果我正在制作这个节目,我会做出以下更改,

  • 活动签名

首先它有两个参数Object,并EventArgs在这里你只需要Car在处理方法(和Random下面讨论的原因).

  • 我将传递LicenseServiceChild的构造函数,并仅在构造函数中注册(subscribe)Event.那将是更清洁的方式.
  • CarName在父类中创建一个字符串成员,这样每个孩子都可以在任何他们想要的地方使用它.
  • 我还没有在这段代码中做过的事情,我永远不会说出一个事件Buy,而是我会命名它Bought.
  • (这仅适用于此场景)在您的代码中,GenerateLicense()Random每次都在创建新对象.因此,如果您对该方法的两次调用都在很短的时间内,它将为两个调用生成相同的随机数.为什么?看到这个问题 - 或者您可以自己尝试下面的示例代码.因此,我将通过已经建立的对象RandomGenerateLicense().因此,Random对于该方法的每次调用都会很常见.

用于解释随机数行为的示例代码

        //as object of Random numbers are different,
        //they will generate same numbers
        Random r1 = new Random();
        for(int i = 0; i < 5; i++)
            Console.WriteLine(r1.Next(0, 9));
        Random r2 = new Random();
        for(int i = 0; i < 5; i++)
            Console.WriteLine(r2.Next(0, 9));
Run Code Online (Sandbox Code Playgroud)

更新

  • 正如Mrinal Kamboj所建议的那样(在下面的评论中),我们不应该Events暴露于外部代码.在这个答案中添加他的评论

两点,EventHandler Buy不允许在外面直接访问它应该是私有的,因为任何人都可以将其设置为null并且所有订阅都消失了.它需要一个事件访问器,因此可以使用+=-=运算符访问事件,并且它本身可以为多个订户提供线程安全,否则会出现竞争条件,请查看一个简单的示例

以下是代码,

你的班级结构:

internal delegate void EventHandler(Car car, Random rnd);
internal class Car
{
    internal string CarName;
    internal virtual void SetName()
    {
        this.CarName = "car";
    }

    //Edit : As Mrinal Kamboj suggested in comments below
    //here keeping event Buy as private will prevent it to be used from external code
    private event EventHandler Buy;
    //while having EventAccessros internal (or public) will expose the way to subscribe/unsubscribe it
    internal event EventHandler BuyAccessor
    {
        add 
        {
            lock (this)
            {
                Buy += value;
            }
        }
        remove
        {
            lock (this)
            {
                Buy -= value;
            }
        }
    }

    internal virtual void OnBuy(Random rnd)
    {
        if (Buy != null)
            Buy(this, rnd);
    }
}

internal class Sport: Car
{
    LicenseService m_ls;
    internal Sport(LicenseService ls)
    {
        this.m_ls = ls;
        this.BuyAccessor += ls.GenerateLicense;
        SetName();
    }

    internal override void SetName()
    {
        this.CarName = "Sport";
    }
}

internal class City: Car
{
    LicenseService m_ls;
    internal City(LicenseService ls)
    {
        this.m_ls = ls;
        this.BuyAccessor += ls.GenerateLicense;
        SetName();
    }
    internal override void SetName()
    {
        this.CarName = "City";
    }
}
Run Code Online (Sandbox Code Playgroud)

LicenseService

internal class LicenseService
{
    internal void GenerateLicense(Car sender, Random rnd)
    {
        string carName = sender.CarName;
        string licenseNumber = "";
        for(int i = 0; i < 5; i++)
        {
            licenseNumber += rnd.Next(0, 9).ToString();
        }
        Console.WriteLine("{1} Car has been bought, this is the license number: {0}", licenseNumber, carName);
    } 
}
Run Code Online (Sandbox Code Playgroud)

并呼吁流动

static void Main(string[] args)
{
    Random rnd = new Random();
    LicenseService ls = new LicenseService();
    Sport sport = new Sport(ls);
    City city = new City(ls);

    city.OnBuy(rnd);
    sport.OnBuy(rnd);

    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)