0

Consider the following example:

[assembly:CLSCompliant(true)]

public interface I
{
    void FrobnicateSQL();
}

public class C : I
{
    public void FrobnicateSql() { }  // *
    void I.FrobnicateSQL() { }
}

The line marked with * yields the following compiler warning:

CS3005: Identifier 'C.FrobnicateSql()' differing only in case is not CLS-compliant

Now, I understand that members visible to other assemblies should not differ only in case, because, for example, case-insensitive languages such as VB.NET would not be able to decide which method to call.

And technically, yes, both members are visible and only differ in case, but they are not visible at the same time: Since FrobnicateSQL is an explicit interface implementation, it's only visible if the static type of the variable is I. The following VB code using the library above compiles correctly and is completely unambiguous:

Dim c As New C()
Dim i As I = c

c.FrobnicateSql()
i.FrobnicateSQL()

Still, the C# compiler treats this as a CLS violation. My question is: Why? Was it a deliberate design decision to include this rare edge case because it makes the specification and/or the algorithm for detecting CLS violations easier? Or did I miss something and the example above can actually cause problems in case-insensitive .NET languages?

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • In case you are wondering *"Why in the world would anyone do something like this?"*: We recently had such a case where some class A uses `FrobnicateSql`, class B uses `FrobnicateSQL` and *years later* we find out that it would be nice to have a common interface for both classes, but we do not want to break backwards-compatibility. – Heinzi Dec 07 '22 at 09:37
  • I think that is because maybe some languages could be case-insensitive. You can check https://www.ecma-international.org/wp-content/uploads/ECMA-335_1st_edition_december_2001.pdf section 9 page 60 – MichaelMao Dec 07 '22 at 09:48
  • @MichaelMao: Indeed. However, I thought I already addressed this in my question... – Heinzi Dec 07 '22 at 09:56
  • 1
    The only thing I can think of, and it's obscure, is that while `private`, the method is still `virtual`, and hence visible to a derived class (not in C#, but on the IL level). In IL it's possible to override a private virtual method, and while explicit interface implementations are also `final` (so this can't legally be done), accessibility and visibility are different things. Even so, since a derived class can't legally do anything with the method that makes it visible even on the IL level (I think), this probably shouldn't count against CLS compliance. – Jeroen Mostert Dec 07 '22 at 10:19
  • Note that VB.NET makes it an *error* to have an explicit interface implementation and a public method differing only in case, notwithstanding that it can consume this C# type. Of course the semantics are different in both languages, since VB.NET allows you to name the explicit implementation while C# does not, and if VB.NET made it legal to call the implementation `I.FrobnicateSQL` it wouldn't have a problem. – Jeroen Mostert Dec 07 '22 at 10:38
  • Rules are a lot easier to explain and interpret if they don't have to list cornercases. – Hans Passant Dec 07 '22 at 11:39
  • "but they are not visible at the same time" -- well, sort of. Not to other classes etc, but as mentioned above, IL generates a virtual method. You can see this e.g. [here](https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgJgIwFgBQyDMABGAHbACmATgGYCGAxmQQJI4DeOBnByALAQGIUA9gCNiYOjXIBlAIoAZABQBKANw4Avjhz5uqAgGECIZmw5ddvAcLESpZaQEcIKgqwIbOAei8EAVOacVkwAdIKi4pIyCq7uWtgaQA=) – BurnsBA Dec 07 '22 at 14:47

0 Answers0