32 bit implicit conversions fail generic overload resolution - c#

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.

Related

Create a method that can only take T as a parameter

I want to create a method that can only take a double as an argument.
If I write it like this:
public static void Foo(double d)
{
// ...
}
Then we can call Foo with ints, floats, doubles, etc. Like this:
public static void Main()
{
Foo(34); // int
Foo(1.3454365F); // float
Foo(34.12); // double
}
I tried to create a generic method that can only take a double as a parameter, like this:
public static void Foo<T>(T t) where T : double
{
// ...
}
But type double can not be used as a constraint.
How can I achieve this?
You can devise some types with implicit operators such that overload resolution will fail to decide a winner for some types but not others:
struct EvilDoubleWrapper
{
public double Value { get; }
public EvilDoubleWrapper(double value)
{
Value = value;
}
public static implicit operator EvilDoubleWrapper(double value) => new EvilDoubleWrapper(value);
public static implicit operator EvilDoubleWrapper(int value)
{
// If the program changes in such a way that this conversion is allowed to occur,
// we should at a minimum raise an assertion
Trace.Assert(true);
return default;
}
}
struct EvilOtherWrapper
{
public static implicit operator EvilOtherWrapper(int value) => default;
}
static void Main(string[] args)
{
Foo(1.23d); // Works
Foo(1); // Error CS0121: The call is ambiguous
}
static void Foo(EvilDoubleWrapper d)
{
// Use d.Value as your double
}
static void Foo(EvilOtherWrapper o)
{
// Does nothing; only exists to fault the overload resolver
}
Neither of EvilDoubleWrapper nor EvilOtherWrapper is "better" than other other for overload resolution, and since there's an implicit conversion from int to both of them, overload resolution will fail. But there is only one implicit conversion from double to either of those, so overload resolution will successfully pick that one. Define implicit conversions for other built-in conversions that you want to forbid as well -- there's a rather small, closed set of such conversions listed here.

Casting an object with a conversion operator fails

So I have this Object, say DoubleContainer.
public struct DoubleContainer
{
private readonly double _value;
private DoubleContainer(double value)
{
_value = value;
}
public static implicit operator double(DoubleContainer doubleContainer)
{
return doubleContainer._value;
}
public static DoubleContainer Create(double value)
{
return new DoubleContainer(value);
}
}
Casting it works as expected in almost all cases, except where it's passed into a function as an Object.
The following code generates an InvalidCastException if I pass in a DoubleContainer:
public double GetDouble(Object input)
{
return (double)input;
}
I can get it to work if I do use dynamic:
public double GetDouble(Object input)
{
return (double)(dynamic)input;
}
My problem with this solution is that Visual Studio grays out the (dynamic), because it should be redundant, so someone may remove it. Also I don't know if there are any other places in the codebase where this same problem may occur.
Is there anything I can do to my implementation of DoubleContainer what will make my first implementation of GetDouble() work? I tried adding another implicit conversion operator from Object to DoubleContainer, but "user-defined conversions to or from a base class are not allowed"....
You cannot make it work, because you can only unbox boxed struct (this is what you are doing with (double) input) to the exact undelying type, for the reasons best described in this article by Eric Lippert. So whenever you do (double) someObject - it will only work if object is actually a double, not int, not float , not DoubleContainer. If you expect other types - you can better use Convert.ToDouble. For that to work with your type you need it to implement IConvertible:
public struct DoubleContainer : IConvertible
{
private readonly double _value;
private DoubleContainer(double value)
{
_value = value;
}
public static implicit operator double(DoubleContainer doubleContainer)
{
return doubleContainer._value;
}
public static DoubleContainer Create(double value)
{
return new DoubleContainer(value);
}
public double ToDouble(IFormatProvider provider) {
return _value;
}
public bool ToBoolean(IFormatProvider provider) {
// delegate to your double
return ((IConvertible) _value).ToBoolean(provider);
}
// ... rest is skipped ...
Then it will work with
public double GetDouble(Object input)
{
return Convert.ToDouble(input);
}
When casting an object to double, compiler does not know that it should call your existing operator double, so it will not insert any call to your user defined operator double.
When you insert dynamic in the middle, compiler will generate something like "if this reference has an operator double, call it`, so it works.
So, it actually cannot work as long as you cast a System.Object to a double, while code suggested by #juharr, slightly modified, will work:
public double GetDouble(Object input)
{
if (input is DoubleContainer)
{
var dc = (DoubleContainer)input;
return (double)dc;
}
return (double)input;
}
EDIT: modified code as per #SergiyKlimkov comment

Implicit conversion fails when changing struct to sealed class

Struct/class in question:
public struct HttpMethod
{
public static readonly HttpMethod Get = new HttpMethod("GET");
public static readonly HttpMethod Post = new HttpMethod("POST");
public static readonly HttpMethod Put = new HttpMethod("PUT");
public static readonly HttpMethod Patch = new HttpMethod("PATCH");
public static readonly HttpMethod Delete = new HttpMethod("DELETE");
private string _name;
public HttpMethod(string name)
{
// validation of name
_name = name.ToUpper();
}
public static implicit operator string(HttpMethod method)
{
return method._name;
}
public static implicit operator HttpMethod(string method)
{
return new HttpMethod(method);
}
public static bool IsValidHttpMethod(string method)
{
// ...
}
public override bool Equals(object obj)
{
// ...
}
public override int GetHashCode()
{
return _name.GetHashCode();
}
public override string ToString()
{
return _name;
}
}
The following code triggers the issue:
public class HttpRoute
{
public string Prefix { get; }
public HttpMethod[] Methods { get; }
public HttpRoute(string pattern, params HttpMethod[] methods)
{
if (pattern == null) throw new ArgumentNullException(nameof(pattern));
Prefix = pattern;
Methods = methods ?? new HttpMethod[0];
}
public bool CanAccept(HttpListenerRequest request)
{
return Methods.Contains(request.HttpMethod) && request.Url.AbsolutePath.StartsWith(Prefix);
}
}
The compiler error is created by changing the HttpMethod struct into a sealed class. The error is reported for return Methods.Contains(request.HttpMethod), note: request.HttpMethod in this case is a string. Which produces the following:
Error CS1929 'HttpMethod[]' does not contain a definition for 'Contains' and the best extension method overload 'Queryable.Contains<string>(IQueryable<string>, string)' requires a receiver of type 'IQueryable<string>'
My question is why? I can redesign the code to make it work, but I'm wanting to know why changing from struct to sealed class creates this weird error.
Edit: Adding a simplified set of example code (available here: https://dotnetfiddle.net/IZ9OXg). Take note that commenting out the implicit operator to string on the second class allows the code to compile:
public static void Main()
{
HttpMethod1[] Methods1 = new HttpMethod1[10];
HttpMethod2[] Methods2 = new HttpMethod2[10];
var res1 = Methods1.Contains("blah"); //works
var res2 = Methods2.Contains("blah"); //doesn't work
}
public struct HttpMethod1
{
public static implicit operator HttpMethod1(string method)
{
return new HttpMethod1();
}
public static implicit operator string (HttpMethod1 method)
{
return "";
}
}
public class HttpMethod2
{
public static implicit operator HttpMethod2(string method)
{
return new HttpMethod2();
}
//Comment out this method and it works fine
public static implicit operator string (HttpMethod2 method)
{
return "";
}
}
Things I know:
Plainly the problem is in type inference.
In the first case, T is deduced to be HttpMethod1.
In the struct case, there is no conversion from HttpMethod1[] to IEnumerable<string> because covariance only works on reference types.
In the class case, there is no conversion from HttpMethod2[] to IEnumerable<string> because covariance only works on reference conversions, and this is a user-defined conversion.
Things I suspect but need to confirm:
Something about the slight difference between my last two points is confusing the type inference algorithm.
UPDATE:
It has nothing to do with covariant array conversions. The problem repros even without array conversions.
It does however have to do with covariant interface conversions.
It has nothing to do with strings. (Strings are often a bit weird because they have a hard-to-remember conversion to IEnumerable<char> that occasionally messes up type inference.)
Here's a program fragment that displays the problem; update your conversions to convert to C instead of string:
public interface IFoo<out T> {}
public class C {}
public class Program
{
public static bool Contains<T>(IFoo<T> items, T item)
{
System.Console.WriteLine(typeof(T));
return true;
}
public static void Main()
{
IFoo<HttpMethod1> m1 = null;
IFoo<HttpMethod2> m2 = null;
var res1 = Contains(m1, new C()); //works
var res2 = Contains(m2, new C()); //doesn't work
}
}
This looks like a possible bug in type inference, and if it is, it is my fault; many apologies if that is the case. Sadly I do not have time to look into it further today. You might want to open an issue on github and have someone who still does this for a living look into it. I would be fascinated to learn what the result was, and if it turns out to be a bug in either the design or the implementation of the inference algorithm.
Firstly, this is an observed behavioural difference between structs and classes. The fact that you have 'sealed' your class does not affect the outcome in this scenario.
Also we know the following statement will compile as expected for HttpMethod type declared as both a struct and class, thanks to the implicit operator.
string method = HttpMethods[0];
Dealing with Arrays introduces some lesser understood compiler nuances.
Covariance
When HttpMethod is a class (reference type), with an array such as HttpRoute.HttpMethods Array covariance (12.5 C# 5.0 Language Spec) comes into play that allows HttpMethod[x] to be treated as an object. Covariance will respect inbuilt implicit reference conversions (such as type inheritance or conversion to object) and it will respect explicit operators, but it will not respect or look for user defined implicit operators. (While a bit ambigous the actual spec doc lists specifically default implicit operators and explicit operators, it does not mention the user defined operators but seeing everything else is so highly specified you can infer that user defined operators are not supported.)
Basically Covariance takes precedence over many generic type evaluations. More on this in a moment.
Array covariance specifically does not extend to arrays of value-types. For example, no conversion exists that permits an int[] to be treated as an object[].
So when HttpMethod is a struct (value type), covariance is no longer an issue and the following generic extension from System.Linq namespace will apply:
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value);
Because you have passed in a string comparator, the Contains statement will be evaluated as follows:
public static bool Contains<string>(this IEnumerable<string> source, string value);
When HttpMethod is a class (Reference Type), thanks to covariance, HttpMethod[] in it's current form comparable only with Object[] and thus IEnumerable, but not IEnumerable< T >, Why not? because the compiler needs to be able to determine the type to generate the generic implementation of IEnumerable< T > and to determine if it can perform an explicit cast from object to T.
Put another way, Compiler cannot determine if T can definetly be a String or not, so it doesn't find the match in the Linq extension methods that we were expecting.
So what can you do about it? (! Not this !)
The first common attempt might be to try using .Cast< string >() to cast the HttpMethod instances to strings for the comparison:
return HttpMethods.Cast<string>().Contains(request.Method) && request.Url.AbsolutePath.StartsWith(Prefix);
You will find that this does not work. Even though The parameter for Cast< T > is of type IEnumerable, not IEnumerable< T >. It is provided to allow you to use older collections that do not implement the generic version of IEnumerable with LINQ. Cast< T > is only designed to convert non-generic objects to their "true" type through the process of evaluating common origins for reference types or Un-Boxing for value types. If Boxing and Unboxing (C# Programming Guide) only applies to value types (structs) and since our HttpMethod type is a reference type (class) the only common origin between HttpMethod and String is Object. On HttpMethod there is no implicit, or even explicit operator that accepts Object and as it is not a value type there is no in built un-box operator that the compiler can use.
Note that this Cast<> will fail at runtime in this scenario when HttpMethod is a value type (class) the compiler will be happy to let it build.
Final Workaround
Instead of Cast< T > or relying on implicit conversions we will need to force the elements in the HttpMethods array to be explicitly cast to string (This will still use out implicit operator!) but Linq again makes this a trivial, but necessary task:
return HttpMethods.Select(c => (string)c).Contains(request.Method) && request.Url.AbsolutePath.StartsWith(Prefix);

C# Cannot overload + if second parameter is an enum

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);
}
}

Why does Delegate.CreateDelegate allow for invalid conversions?

Edit: I filed a bug report on microsoft connect::
https://connect.microsoft.com/VisualStudio/feedback/details/614234/delegate-createdelegate-allows-binding-functions-with-enum-parameters-to-the-enum-base-type#details
Consider the following thing:
public static Int32 Test(Int16 #short)
{
return #short;
}
and from calling code::
Func<Int16,Int32> test = Test; //valid method group conversion.
Func<Int16,StringComparison> test2 = Test; // doesn't compile, not valid method group conversion.
Func<Int16,StringComparison> test3 = test; // doesn't compile, invalid contra-variance.
Func<Int16,StringComparison> test4 = Delegate.CreateDelegate(typeof(Func<Int16,StringComparison>),test.Method) as Func<Int16,StringComparison>; // works fine.
Why can Delegate.CreateDelegate do this strange conversion that no other means of creating a function allows? Even worse, a somewhat more sensible conversion to say Int64 or Object both fail. I realize that StringComparison "extends" Int32, But I thought that was more of a compiler trick as enum's extend the Enum class.
Also, this conversion works for DynamicMethod.CreateDelegate, as well.
Edit just tried it with Nullable<Int32> it doesn't work, which is perplexing. I think the only "free" conversion is to Enum types to their underlying type, but why?
Note that this does not allow you to convert an int instance method (like GetHashCode) to an open method that takes the enum type, which is why I think this is a bug as the behavior is inconsistent.
Edit:
If we remove test2 and test3 line we can then test to see that the method,delegate, and the "illegal" delegate all work as expected.
Console.WriteLine(Test(0)); // prints 0
Console.WriteLine(test(0)); // prints 0
Console.WriteLine(test4(0)); //prints CurrentCulture
Edit:
Here is a very big abuse of this that I wrote in about 10 minutes. This creates an IEqualityComparer<T> for an TEnum, by basically grabbing the one for its an underlying type and then just wrapping the Equals, and HashCode and using this trick/abuse to convert the parameters to TEnums, rather than underlying type. If this is a bug I'd like to know so that I won't try to rely on this behavior.
class EnumComparer<TEnum> : EqualityComparer<TEnum> where TEnum : struct
{
static Func<TEnum, TEnum, bool> s_Equals;
static Func<TEnum, int> s_HashCode;
static EnumComparer<TEnum> s_default;
static EnumComparer()
{
if (!typeof(TEnum).IsEnum) throw new Exception("Not an enum type");
Type underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
object equalityComparer = typeof(EqualityComparer<>).MakeGenericType(new[] { underlyingType }).GetProperty("Default").GetGetMethod().Invoke(null, null);
s_Equals = Delegate.CreateDelegate(typeof(Func<TEnum, TEnum, bool>), equalityComparer,equalityComparer.GetType().GetMethod("Equals", new[]{underlyingType,underlyingType})) as Func<TEnum,TEnum,bool>;
s_HashCode = Delegate.CreateDelegate(typeof(Func<TEnum, int>), equalityComparer, equalityComparer.GetType().GetMethod("GetHashCode", new[]{underlyingType})) as Func<TEnum, int>;
s_default = new EnumComparer<TEnum>();
}
public static new EnumComparer<TEnum> Default
{
get
{
return s_default;
}
}
public override bool Equals(TEnum x, TEnum y)
{
return s_Equals(x, y);
}
public override int GetHashCode(TEnum obj)
{
return s_HashCode(obj);
}
private EnumComparer()
{
}
}
This is not a bug. It is always possible to convert an integral value type to an enum like StringComparison. The compiler normally requires a cast but you are bypassing the compiler here. And just as in C#, there is no check to verify that the integral value actually represents one of the enumeration values.

Categories