Repository navigation
CS0111 False Positive on Extension Member #137202
|
Hi all, Running into an unexpected CS0111 error when using extension members. Take this minimal repro I put together inspired by the using System;
using System.Numerics;
public static class Extensions
{
// Convert from TSmall to TLarge.
internal static TLarge ImplFrom<TSmall, TLarge>(TSmall small)
where TSmall : INumberBase<TSmall>, IMinMaxValue<TSmall>
where TLarge : INumberBase<TLarge>, IMinMaxValue<TLarge>
{
throw new NotImplementedException();
}
extension(ushort)
{
public static ushort From(byte small) => Extensions.ImplFrom<byte, ushort>(small);
}
extension(uint)
{
public static uint From(byte small) => Extensions.ImplFrom<byte, uint>(small); // CS0111: Type 'Extensions' already has a member called 'From' with the same parameter types.
public static uint From(ushort small) => Extensions.ImplFrom<ushort, uint>(small);
}
}If the above code were allowed to compile, we'd get the following (comments own): public static class Extensions
{
// Extension class for `extension(ushort)`
[SpecialName]
[Extension]
public sealed class <G>$B7... { }
// Extension class for `extension(uint)`
[SpecialName]
[Extension]
public sealed class <G>$B7... { }
[NullableContext(1)]
internal static TLarge ImplFrom<[Nullable(0)] TSmall, [Nullable(0)] TLarge(TSmall small where TSmall : INumberBase<TSmall, IMinMaxValue<TSmall> where TLarge : INumberBase<TLarge>, IMinMaxValue<TLarge>
{
throw new NotImplementedException();
}
// byte -> ushort from extension(ushort)
public static ushort From(byte small)
{
return ImplFrom<byte, ushort>(small);
}
// byte -> uint from extension(uint)
public static uint From(byte small)
{
return ImplFrom<byte, ushort>(small);
}
// ushort -> uint from extension(uint)
public static uint From(ushort small)
{
return ImplFrom<ushort, uint>(small);
}
}However, for whatever reason, it detects Commenting out either Am I missing something as to why this diagnostic would be raised? Is it truly a false positive or is there a factor I'm not seeing here that makes the diagnostic entirely valid (if not a little lacking in detail)? |
Replies: 1 comment
|
As you saw, extensions are lowered to normal methods in the extension class. And normal C# rules about overload resolution apply. This means that multiple methods with the same parameter-set "signature" can't exist. And in your case, public static class LegacyExtensions
{
public static ushort From(byte x) { ... }
public static uint From(byte x) { ... }
}You need the extension for You'd need to do this: using System;
using System.Numerics;
public static class UShortExtensions
{
extension(ushort)
{
public static ushort From(byte small) => Impl.From<byte, ushort>(small);
}
}
public static class UIntExtesions
{
extension(uint)
{
public static uint From(byte small) => Impl.From<byte, uint>(small);
public static uint From(ushort small) => Impl.From<ushort, uint>(small);
}
}
file static class Impl
{
// Convert from TSmall to TLarge.
public static TLarge From<TSmall, TLarge>(TSmall small)
where TSmall : INumberBase<TSmall>, IMinMaxValue<TSmall>
where TLarge : INumberBase<TLarge>, IMinMaxValue<TLarge>
{
throw new NotImplementedException();
}
}Aside: there is a wrinkle here with "same parameter-set" when it comes to implicit/explicit conversion operators (they're allowed despite the apparent conflict), but the standard allows such a thing. |
As you saw, extensions are lowered to normal methods in the extension class. And normal C# rules about overload resolution apply. This means that multiple methods with the same parameter-set "signature" can't exist. And in your case,
From(byte) -> ushortandFrom(byte) -> uinthave the same parameter-set. It's the same reason this doesn't compile:You need the extension for
ushortto be in a separate extension class than that ofuint.You'd need to do this: