rspec/rules/S4015/csharp/rule.adoc
2023-06-13 12:23:03 +02:00

107 lines
2.9 KiB
Plaintext

== Why is this an issue?
Decreasing the https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers[accessibility level] of an inherited method that is not https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override[overridable] to https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/private[private] will shadow the name of the base method and can lead to confusion.
[source,csharp]
----
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
private void SomeMethod(int count) { } // Noncompliant
}
class Program
{
public void DoWork()
{
var derived = new Derived();
derived.SomeMethod(42); // Base.SomeMethod is accessed here
}
}
----
Another potential problem is the case of a class deriving from `Derived` and accessing `SomeMethod`. In this scenario, the method accessed will instead be the `Base` implementation, which might not be what was expected.
[source,csharp]
----
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
private void SomeMethod(int count) { } // Noncompliant
}
public class SecondDerived : Derived
{
public void DoWork()
{
SomeMethod(42); // Base.SomeMethod is accessed here
}
}
----
One way to mitigate this, is by sealing the `Derived` class by using the https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/sealed[sealed] modifier, thus preventing inheritance from this point on.
[source,csharp]
----
public class Base
{
public void SomeMethod(int count) { }
}
public sealed class Derived : Base
{
private void SomeMethod(int count) { } // Compliant: class is marked as sealed
}
----
Another way to mitigate this, is by having the `Derived` implementation match the https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers[accessibility] modifier of the `Base` implementation of `SomeMethod`. From a caller's perspective, this is closer to the expected behavior.
[source,csharp]
----
using System;
namespace MyLibrary
{
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
public void SomeMethod(int count) { } // Compliant: same accessibility as Base.SomeMethod
}
public class Program
{
public void DoWork()
{
var derived = new Derived();
derived.SomeMethod(42); // Derived.SomeMethod is called
}
}
}
----
Last but not least, consider using a different name for the `Derived` method, thus completely eliminating any confusion caused by the naming collision.
[source,csharp]
----
public class Base
{
public void SomeMethod(int count) { }
}
public class Derived : Base
{
private void SomeOtherMethod(int count) { } // Compliant
}
----
include::../resources.adoc[]
include::../rspecator.adoc[]