This code is not compiled:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar
{
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(TOut i) => null;
}
// Here compiler complains:
// CS0029 Cannot implicitly convert type 'int' to 'Bar'
// CS1662 Cannot convert lambda expression to intended delegate type
// because some of the return types in the block
// are not implicitly convertible to the delegate return type
Foo.Do(() => 1);
My expectation would be that compiler sees the return type of the lambda and that no valid overload can be selected unless int is converted to Bar<int>.
However, I see that compiler resolves to the first method.
Which part of spec defines this behavior?
This is specified in Method Invocations, when the spec is talking about what method declarations count as a candidate for overload resolution, for an invocation of the form M(A):
The set of candidate methods for the method invocation is constructed. For each method F associated with the method group M:
If F is non-generic, F is a candidate when:
M has no type argument list, and
F is applicable with respect to A.
If F is generic and M has no type argument list, F is a candidate when:
Type inference succeeds, inferring a list of type arguments for the call, and
[...]
Just from those rules, we can see that the non-generic Do is a candidate, and the generic Do is not, because type inference fails. Try commenting out the non-generic Do, and you will see that it says something like "type arguments cannot be inferred".
I do not have the full answer, but I have some observations:
Observation 1: With removing the non-generic version of Do:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
, still the compiler can't resolve Func<Bar<TOut>> f. So it does not seem that the issue is about picking the wrong overload, but rather compiler not being able to match () => 1 to Func<Bar<TOut>> f implicitly at all.
Observation 2: The code below works
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
Foo.Do(() => 1);
which shows the compiler is checking implicit conversions.
Observation 3: Making the implicit cast operator to take int as in input, eventhough makes the operator not practically usable as TOut will not be resolved, makes the compiler find the operator:
static class Foo
{
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(int i) => null; // Input is `int` now
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
So all of this combined makes me think that the compiler is just not trying to which generic types would cause the implicit conversion to work.
Related
I've been experimenting with functional programming concepts for C# and encountered with the Either monad, which in F# would be Choice if I'm not mistaken. C# doesn't have this type natively implemented and I've come with a very naive implementation of it.
// Don't use this code in production, this is just an experiment type
public record Either<T1, T2> where T1: class where T2 : class
{
public static implicit operator Either<T1, T2>(T1 typeOne)
=> (Either<T1, T2>)(object)typeOne;
public static implicit operator Either<T1, T2>(T2 typeTwo)
=> (Either<T1, T2>)(object)typeTwo;
public static implicit operator T1(Either<T1, T2> either)
=> (T1)(object)either;
public static implicit operator T2(Either<T1, T2> either)
=> (T2)(object)either;
}
The compiler accepts any implicit conversion I do with the types defined when constructing the generic.
// Accepted by the compiler
Either<Foo, Bar> someBar = new Bar();
Bar actualBar = someBar;
// Rejected by the compiler as expected
Either<Foo, Bar> wrong = new MyClass();
But it doesn't accept pattern matching the same type with the respective generics
_ = someBar switch
{
// Error: an expression of type 'Either<Foo, Bar>' cannot be handled by a pattern of type 'Foo'
Foo => Console.WriteLine("Foo works!"),
// Error: an expression of type 'Either<Foo, Bar>' cannot be handled by a pattern of type 'Bar'
Bar => Console.WriteLine("Bar works!"),
// Works fine
null => Console.WriteLine("Null works!")
}
I've done some research and the only requirement I found for a type to use pattern matching is having implicit or explicit conversions, not sure if this is right but I found not only in this answer, but in others too.
How am I supposed to design my generic type to work with pattern matching?
In short - you can't. Type patterns require an implicit reference conversion to exist between two types:
The implicit reference conversions are those conversions between reference_types that can be proven to always succeed, and therefore require no checks at run-time.
Reference conversions, implicit or explicit, never change the referential identity of the object being converted. In other words, while a reference conversion may change the type of the reference, it never changes the type or value of the object being referred to.
Which does not include user defined conversions.
Naive approach (if you don't want to look into exisiting implementations like Either from language-ext or OneOf) can look something like this:
public record Either<T1, T2> where T1: class where T2 : class
{
public T1 Left { get; set; }
public T2 Right { get; set; }
public bool IsLeft { get; }
}
And:
var x = someBar switch
{
{IsLeft: true} => someBar.Left
};
I'm experimenting with custom integer types and came across an interesting issue involving generics, implicit conversions, and 32 bit integers.
Below is a stripped down example of how to reproduce the problem. If I have two implicit methods that convert int to MyInt and vice versa, I get a compilation error which looks like C# can't resolve which generic type to use. And it only happens with int or uint. All other integer types work fine: sbyte,byte,short,ushort,long,ulong.
If I remove one of the implicit conversion methods, it also works fine. Something to do with circular implicit conversions?
using Xunit;
public class MyInt
{
public int Value;
//If I remove either one of the implicit methods below, it all works fine.
public static implicit operator int(MyInt myInt)
{
return myInt.Value;
}
public static implicit operator MyInt(int i)
{
return new MyInt() { Value = i };
}
public override bool Equals(object obj)
{
if (obj is MyInt myInt)
{
return this.Value == myInt.Value;
}
else
{
int other_int = (int)obj;
return Value == other_int;
}
}
}
Below is the test code showing the compilation errors I get when both implicit methods are defined.
public class Test
{
[Fact]
public void EqualityTest()
{
MyInt myInt = new MyInt();
myInt.Value = 4 ;
Assert.Equal(4, myInt.Value); //Always OK which makes sense
//Compile errors when both implicit methods defined:
// Error CS1503 Argument 1: cannot convert from 'int' to 'string',
// Error CS1503 Argument 2: cannot convert from 'ImplicitConversion.MyInt' to 'string'
Assert.Equal(4, myInt);
}
}
I believe C# is complaining about not being able to convert both types to string as that is the type of the last Xunit.Assert.Equal() overload and all the others failed to match:
//Xunit.Assert.Equal methods:
public static void Equal<T>(T expected, T actual);
public static void Equal(double expected, double actual, int precision);
public static void Equal<T>(T expected, T actual, IEqualityComparer<T> comparer);
public static void Equal(decimal expected, decimal actual, int precision);
public static void Equal(DateTime expected, DateTime actual, TimeSpan precision);
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual, IEqualityComparer<T> comparer);
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual);
public static void Equal(string expected, string actual, bool ignoreCase = false, bool ignoreLineEndingDifferences = false, bool ignoreWhiteSpaceDifferences = false);
public static void Equal(string expected, string actual);
I don't think I've made a mistake with the implicit conversions as I can make other similar examples create the same problem when used with 32 bit ints.
I'm testing in a .NET Core 3.0 project.
Any help would be appreciated. Thanks!
Clarification:
What I would like to know is why this only fails with 32 bit integers. Implicit conversions are working (confirmed with debugging) when the types are anything else like the example below using a long.
using Xunit;
public class MyLong
{
public long Value;
public static implicit operator long(MyLong myInt)
{
return myInt.Value;
}
public static implicit operator MyLong(long i)
{
return new MyLong() { Value = i };
}
public override bool Equals(object obj)
{
if (obj is MyLong myInt)
{
return this.Value == myInt.Value;
}
else
{
long other_int = (long)obj;
return Value == other_int;
}
}
}
public class Test2
{
[Fact]
public void EqualityTest()
{
MyLong myLong = new MyLong();
myLong.Value = 4 ;
Assert.Equal(4, myLong); //NOTE! `4` is implicitly converted to a MyLong
//object for comparison. Confirmed with debugging.
}
}
Something to do with circular implicit conversions?
Yes (though, you've already demonstrated that much by showing that it works fine when one of the conversions is eliminated).
The reason this is happening with int, and not with other types, is that the type of your literal is int. This means that during overload resolution, the compiler can go either way: convert int to MyInt, or convert MyInt to int. Neither option is clearly "better" than the other, so neither of those conversions survive consideration.
Then, having ruled out the closest possible generic version of the method, of the remaining overloads available the only one left is the Equal(string, string) overload (the only other one left with just two parameters is the Equal<T>(IEnumerable<T>, IEnumerable<T>), which is "worse" than the Equal(string, string) overload according to the overload resolution rules). Having found exactly one method that is clearly "better" than any others, the compiler then tries to use that method with your parameters, which of course don't fit, causing the errors to be emitted.
On the other hand…
When you try to call Equal(4, myLong), you've got two incompatible types. A literal having type int, and the MyLong value. In this case, the compiler tries each parameter one by one and finds that when it uses the MyLong type as the type parameter, it is possible to promote the int literal to a long and then implicitly convert that to MyLong. But it can't go the other way. It's not possible to select int as the generic type parameter, because MyLong can't be implicitly converted to int. So in that case, there is a "better" overload to choose, and so it's chosen.
By explicitly specifying the literal's type, you can try different combinations and see this pattern at work. First, I prefer a simpler wrapper class to test with:
public class Wrapper<T>
{
public T Value;
public static implicit operator T(Wrapper<T> wrapper) => wrapper.Value;
public static implicit operator Wrapper<T>(T value) => new Wrapper<T> { Value = value };
}
Then try these:
Wrapper<int> w1 = new Wrapper<int> { Value = 4 };
Wrapper<long> w2 = new Wrapper<long> { Value = 4 };
Assert.Equal(4, w1); // error
Assert.Equal((short)4, w1); // no error
Assert.Equal(4, w2); // no error
Assert.Equal(4L, w2); // error
The only thing that makes int special is that that's the default type for the numeric literal. Otherwise, a type that wraps int works exactly the same as a type that wraps anything else. As long as a conversion is available only in one direction between the two parameters, everything's fine. But when the conversion is available in both directions, the compiler has no choice but to throw up its hands and give up.
I am trying understand OOP concepts in C#.
In the following sample code:
why the ins1 prefers generic method
why the ins2, ins3 prefers non-generic method
Note:when I comment out either of the "MyTestMethod" method, still the program continues to run successfully. This snippet is not something from production. This is just my training sample. So, please do not mind the naming conventions and standards.
using System;
namespace ConsoleApplication1
{
class Program
{
public static void MyTestMethod(J input)
{
Console.WriteLine($"Program.MyTestMethod: {input.Val}");
}
public static void MyTestMethod<T>(T input) where T : J
{
Console.WriteLine($"Program.MyTestMethod<T>: {input.Val}");
}
static void Main(string[] args)
{
J2 ins1 = new J2(1);
MyTestMethod(ins1);
J ins2 = new J(2);
MyTestMethod(ins2);
J ins3 = new J2(3);
MyTestMethod(ins3);
Console.ReadKey();
}
}
internal class J
{
public int Val { get; set; }
public J(int i)
{
Console.WriteLine($"concrete base {i}");
Val = i;
}
}
internal class J2 : J
{
public J2(int i) : base(i * -1)
{
Console.WriteLine($"concrete {i}");
}
}
}
Section 7.5.3.2 of the C# specification is the relevant part here - "Better function member".
The result is more simply demonstrated as:
using System;
class Test
{
static void Foo<T>(T item)
{
Console.WriteLine("Generic");
}
static void Foo(object x)
{
Console.WriteLine("Non-generic");
}
static void Main()
{
Foo(new object()); // Calls Foo(object)
Foo("test"); // Calls Foo<T>(T)
}
}
In both calls, both overloads are applicable function members. In picking which overload to call, the compiler first checks which conversion from argument type (or expression) to parameter type is "better".
When the type of the argument is object, T is inferred to be object as well, and so for both candidates the conversion is the identity conversion of object to object. At that point, the tie-breaking rules of 7.5.3.2 get involved, and the first one is:
If MP is a non-generic method and MQ is a generic method, then MP is better than MQ.
So that's why the non-generic overload is picked in that case.
When the argument is of type string, T is inferred to be string, so we have to compare the conversion from string to string (for the generic method) to the conversion from string to object (for the non-generic method). Here section 7.5.3.3 of the C# spec comes in, which starts:
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
E has a type S and an identity conversion exists from S to T1 but not from S to T2
In this context, E is the expression "test", S is the type string, T1 is the type string, T2 is the type object, and so the conversion from string to string is deemed better - and the generic method is picked.
why the ins2,3 prefers non-generic method
Because in both cases, the type of the variable (not the instantiated type) is J, and that is an exact match for your method with signature MyTestMethod(J input). Hence, the overload resolution rules tells it takes that one.
why the ins1 prefers generic method
Since T can be J2 in the method MyTestMethod(T input) where T : J, and it can strongly type T to be J2, again, it is a better match than the base class J, from your other method. So it matches the method with type arguments.
I have the following signature for overloaded +:
public static double operator +(MyClass x, MyEnum e)
and an expression of the form:
x.Value = someMyClassValue + MyEnum.X;
The behavior the debugger shows is as if the expression had been:
x.Value = MyEnum.X;
The overload never gets called.
I also have:
public static double operator +(MyClass x, object o)
but that doesn't get called either for enums, though it does for other cases.
I also have overloads for string, int, float, double, and they all work perfectly. Why is enum a special case, and why the odd behavior? Could this be a bug in the Mono compiler?
I'm using Mono 2.10.8.1 on Ubuntu 13.04.
Afternote
The problem was that I had also defined an implicit cast to int. See my answer for details.
The problem was that I had also defined:
public static implicit operator int(MyClass o)
The implicit cast takes precedence over the overloaded operator, and the whole addition expression takes the type of the enum.
Since I wanted to keep the implicit cast to int, I adopted this solution:
public enum MyEnum : ulong
With that, the cast to int no longer takes place.
The following program demonstrates the problem I was having. It's output is "SECOND" instead of "OK".
using System;
public class EnumPlus
{
public enum Constant
{
FIRST,
SECOND
};
// if this implicit cast is removed the result is what I expected
public static implicit operator int(EnumPlus f)
{
return 1;
}
public static string operator+(EnumPlus o, int i)
{
Console.WriteLine("operator + called for int");
return "BAD";
}
public static string operator+(EnumPlus o, Constant Constant)
{
Console.WriteLine("operator + called for enum");
return "OK";
}
public static void Main()
{
EnumPlus o = new EnumPlus();
Console.WriteLine(o + Constant.FIRST);
}
}
I have a class Thing that is implicitly castable from a string. When I call a method with a Thing parameter directly the cast from string to Thing is done correctly.
However if I use reflection to call the same method it throws the exception
System.ArgumentException : Object of type 'System.String' cannot be
converted to type 'Things.Program+Thing'.
Maybe there is a good reason for this, but I can't figure it out. Does somebody have an idea how to get this working using reflection?
namespace Things
{
class Program
{
public class Thing
{
public string Some;
public static implicit operator Thing(string s)
{
return new Thing {Some = s};
}
}
public void showThing(Thing t)
{
Console.WriteLine("Some = " + t.Some);
}
public void Main()
{
showThing("foo");
MethodInfo showThingReflected = GetType().GetMethod("showThing");
showThingReflected.Invoke(this, new dynamic[] {"foo"});
}
}
}
Meta: Please, no discussions why implicit casting or reflection is bad.
The trick is to realize that the compiler creates a special static method called op_Implicit for your implicit conversion operator.
object arg = "foo";
// Program.showThing(Thing t)
var showThingReflected = GetType().GetMethod("showThing");
// typeof(Thing)
var paramType = showThingReflected.GetParameters()
.Single()
.ParameterType;
// Thing.implicit operator Thing(string s)
var converter = paramType.GetMethod("op_Implicit", new[] { arg.GetType() });
if (converter != null)
arg = converter.Invoke(null, new[] { arg }); // Converter exists: arg = (Thing)"foo";
// showThing(arg)
showThingReflected.Invoke(this, new[] { arg });
Found an answer which uses a TypeConverter (as Saeed mentions)
Seems to do the job.
TypeConverter For Implicit Conversion when using reflection
In this specific case you can make the conversion through the array type, that is
showThingReflected.Invoke(this, new Thing[] {"foo"});
but that's a kind of "cheating". In general, you cannot expect the Invoke to consider your user-defined implicit operator. This conversion must be inferred compile-time.