我正在学习类上的 C# 事件实现。
我有示例案例:
有一个 Sport 和 City 类继承自 Car 类。 Car 类具有由 Sport 和 City 类继承的基方法调用 OnBuy。在 OnBuy 方法内部,事件处理程序已分配给 Event Buy。
还有一个称为 LicenseService 的服务或类,它会在每次购买时生成许可证号。
我在这种情况下实现了事件驱动编程。 这是我的 git 示例:
https://github.com/adityosnrost/CSharpLearningEvent
问题:
这是在 C# 上使用 Event 的正确方法吗?
如果这是正确的。我可以将 OnBuy 方法覆盖到每个 child 吗?以及如果覆盖可用我该怎么办?
我可以做些什么来改进这个样本?
谢谢
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);
}
}
最佳答案
如果我正在制作这个程序,我会做以下更改,
- 事件购买签名
首先,它有两个参数 Object
和 EventArgs
,您只需要在处理程序方法中使用 Car
(还有 Random
下面讨论了为什么)。
- 我会在 Child 的构造函数中传递
LicenseService
,并仅在构造函数中注册(订阅)事件。那将是更清洁的方式。 - 会在父类中创建一个字符串成员
CarName
,这样每个 child 都可以在任何他们想要的地方使用它。 - 还有一件事我没有在此代码中完成,我永远不会将事件命名为
Buy
,而是将其命名为Bought
。 - (这仅适用于此场景)在您的代码中,您每次都在
GenerateLicense()
中创建新的Random
对象。因此,如果您对该方法的两次调用都在短时间内,它将为两次调用生成相同的随机数。为什么?看这个Question - 或者您可以自己尝试下面的示例代码。所以我会在GenerateLicense()
中传递已经创建的Random
对象。所以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));
更新
- 正如 Mrinal Kamboj 所建议的(在下面的评论中),我们不应将
Events
暴露给外部代码。在此答案中也添加他的评论
Two points,
EventHandler Buy
cannot be allowed to be directly accessed outside it shall be private, since anyone otherwise can set that to null and all subscription is gone. It needs an Event Accessor, so that event can be accessed using+=
and-=
operators and there itself you make it thread safe for multiple subscribers, else there will be race condition, check a simple example
代码如下,
你的类(class)结构:
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";
}
}
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);
}
}
并调用流程
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();
}
关于事件的 C# 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50384360/