97 lines
2.6 KiB
Plaintext
97 lines
2.6 KiB
Plaintext
== Why is this an issue?
|
|
|
|
https://learn.microsoft.com/en-us/dotnet/csharp/event-pattern#define-and-raise-field-like-events[Field-like] events are events that do not have explicit https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/add[`add`] and https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/remove[`remove`] accessors.
|
|
[source,csharp]
|
|
----
|
|
public event EventHandler MyEvent; // No add and remove accessors
|
|
----
|
|
The compiler generates a `private` `delegate` field to back the event, as well as generating the implicit `add` and `remove` accessors.
|
|
|
|
When a `virtual` field-like `event` is overridden by another field-like `event`, the behavior of the C# compiler is to generate a new `private` `delegate` field in the derived class, separate from the parent's field. This results in multiple and separate events being created, which is rarely what's actually intended.
|
|
|
|
=== Noncompliant code example
|
|
|
|
[source,csharp,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
abstract class Car
|
|
{
|
|
public virtual event EventHandler OnRefuel; // Noncompliant
|
|
|
|
public void Refuel()
|
|
{
|
|
// This OnRefuel will always be null
|
|
if (OnRefuel != null)
|
|
{
|
|
OnRefuel(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
}
|
|
|
|
class R2 : Car
|
|
{
|
|
public override event EventHandler OnRefuel;
|
|
}
|
|
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
var r2 = new R2();
|
|
r2.OnRefuel += (o, a) =>
|
|
{
|
|
Console.WriteLine("This event will be called");
|
|
};
|
|
r2.Refuel();
|
|
}
|
|
}
|
|
----
|
|
|
|
=== Compliant solution
|
|
|
|
To prevent this, remove the `virtual` designation from the parent class event.
|
|
|
|
[source,csharp,diff-id=1,diff-type=compliant]
|
|
----
|
|
abstract class Car
|
|
{
|
|
public event EventHandler OnRefuel; // Compliant
|
|
|
|
public void Refuel()
|
|
{
|
|
if (OnRefuel != null)
|
|
{
|
|
OnRefuel(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
}
|
|
|
|
class R2 : Car
|
|
{
|
|
|
|
}
|
|
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
var r2 = new R2();
|
|
r2.OnRefuel += (o, a) =>
|
|
{
|
|
Console.WriteLine("This event will be called");
|
|
};
|
|
r2.Refuel();
|
|
}
|
|
}
|
|
----
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/add[Add keyword]
|
|
* https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/remove[Remove keyword]
|
|
* https://learn.microsoft.com/en-us/dotnet/csharp/delegate-class[Delegates]
|
|
* https://learn.microsoft.com/en-us/dotnet/csharp/events-overview[Introduction to events]
|
|
* https://learn.microsoft.com/en-us/dotnet/csharp/event-pattern#define-and-raise-field-like-events[Field-like events]
|
|
|
|
include::../rspecator.adoc[] |