-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Bug] Method extension overloading with generics fails to account for generic constraints #1628
Comments
The CLR doesn't allow for overloading on generic type constraints. They are not a part of the method signature, they're just metadata. |
Could make a funny name for clr when name collisions are resolved via generic constraints public static T Extension<T>(this T) where T : class
{
throw new NotImplementedException();
}
public static T Extension<T>(this T) where T : struct
{
throw new NotImplementedException();
} |
This works in C# 7.3 without any issues. Closing as fixed. |
@gafter ❤️ |
The complaint seems to be about being able to define the extension methods in the same static class as overloads, which is a compiler error. |
@HaloFour Perhaps so, but in C# 7.3 there is a workaround. |
And (the improved overloading) used as a suggested perf workaround... dotnet/corefxlab#2358 (comment) |
using System;
static class Ex
{
public struct Struct { }
public struct Class { }
public static T Extension<T>(this T x, Class _ = default) where T : class
{
throw new NotImplementedException();
}
public static T Extension<T>(this T x, Struct _ = default) where T : struct
{
throw new NotImplementedException();
}
}
class Program
{
static void Main()
{
1.Extension();
"".Extension();
}
} |
Splitting extension versions into different classes is in fact acceptable workaround. |
Not pleasant and not a self-evident workaround. So I will have to create a range of extensions suffixed 1,2,3. And have to move it into top-level class as extension require. using System;
Console.WriteLine();
record A();
record B();
record D();
record C() {
public T To<T>() where T: A => throw new Exception();
public T To<T>() where T: B => throw new Exception();
}
static class CE {
static T To<T>(this C self) where T: D => throw new Exception();
} |
This is not a bug, the runtime does not consider generic type constraints as a part of the method signature and such does not permit overloading based on them. |
As a workaround you can write: using System;
Console.WriteLine();
record A();
record B();
record D();
struct Dummy<T>{}
record C() {
public T To<T>(Dummy<A> dummy = default) where T: A => throw new Exception();
public T To<T>(Dummy<B> dummy = default) where T: B => throw new Exception();
public T To<T>(Dummy<D> dummy = default) where T: D => throw new Exception();
}
static class CE {
static T To<T>(this C self) where T: D => throw new Exception();
} |
No need for a dummy. May end-use that for partial updates/merges (if value not set, take from record A();
record B();
record D();
record C() {
public T To<T>(A _ = default) where T: A => throw new Exception();
public T To<T>(B _ = default) where T: B => throw new Exception();
}
static class CE {
static T To<T>(this C self) where T: D => throw new Exception();
} |
I used an empty struct to reduce the overhead (the compiler may not eliminate the not used parameter...). |
With default hack we get:
|
@dzmitry-lahoda you're commenting on a closed issue. |
Not sure if this fits here, or in Roslyn repo.
Consider following code:
Where TypeOne and TypeTwo inheritance tree meets only on root (object).
This will result in error: Type '...' already defines a member called 'Extension' with the same parameter types.
Expected result: both extension methods are being recognized as differend overloads.
The text was updated successfully, but these errors were encountered: