我正在学习关于类的C#事件实现.
我有例子:
Car class继承了Sport和City课程.Car class具有由Sport和City类继承的基本方法调用OnBuy.在OnBuy方法中,事件处理程序已分配给Event Buy.
还有一个名为LicenseService的服务或类,每次购买时都会生成许可证号.
在这种情况下,我实现了事件驱动编程.这是我的git示例:
https://github.com/adityosnrost/CSharpLearningEvent
问题:
这是在C#上使用Event的正确方法吗?
如果这是正确的.我可以将OnBuy方法覆盖到每个孩子身上吗?如果覆盖可用,我该怎么办?
我可以做些什么来使这个样本更好?
谢谢
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)
如果我正在制作这个节目,我会做出以下更改,
首先它有两个参数Object
,并EventArgs
在这里你只需要Car
在处理方法(和Random
下面讨论的原因).
LicenseService
Child的构造函数,并仅在构造函数中注册(subscribe)Event.那将是更清洁的方式.CarName
在父类中创建一个字符串成员,这样每个孩子都可以在任何他们想要的地方使用它.Buy
,而是我会命名它Bought
.GenerateLicense()
您Random
每次都在创建新对象.因此,如果您对该方法的两次调用都在很短的时间内,它将为两个调用生成相同的随机数.为什么?看到这个问题 - 或者您可以自己尝试下面的示例代码.因此,我将通过已经建立的对象Random
在GenerateLicense()
.因此,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)
更新
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)