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);
}
}
Related
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.
I'm using dynamic types and implicit conversions and am curious why the following code calls the implicit int conversion and not the implicit float conversion within my TestClass?
TestClass myVar = 1.6f;
var result = Mathf.Abs(myVar);
The value assigned from this code is essentially Mathf.Abs(1) rather than Mathf.Abs(1.6f).
For reference, the following is my TestClass which is a contrived class for the purpose of this post.
class TestClass
{
public dynamic variable;
TestClass(dynamic v)
{
variable = v;
}
public static implicit operator float(TestClass v)
{
return (float)v.variable;
}
public static implicit operator int(TestClass v)
{
return (int)v.variable;
}
public static implicit operator TestClass(float v)
{
return new TestClass(v);
}
}
Ultimately I'm creating a class to mirror the functionality of another language which allows variables to be float, string or boolean (without specifically declaring the type) and casts appropriately at run-time depending on the operator or function to which they're being applied. The language in question is one designed to help children learn to code and handles any situation gracefully without crashing. This information isn't really required for the question but gives a general overview of the background.
Firstly, we can reproduce this without needing Unity or dynamic typing. The following example prints 1, calling the PrintValue(int) overloads:
using System;
public class Wrapper
{
private float value;
public Wrapper(float value) => this.value = value;
public static implicit operator float(Wrapper wrapper) =>
wrapper.value;
public static implicit operator int(Wrapper wrapper) =>
(int) wrapper.value;
}
class Test
{
static void Main()
{
Wrapper wrapper = new Wrapper(1.5f);
PrintValue(wrapper);
}
static void PrintValue(float f) => Console.WriteLine(f);
static void PrintValue(int i) => Console.WriteLine(i);
}
The rules of overload resolution are described in the ECMA language specification section 12.6.4. Both overloads of PrintValue are applicable function members because there's an implicit conversion from Wrapper to each of float and int.
In this case, PrintValue(int) ends up as the better function member because the conversion from Wrapper to int is a better conversion from expression than the conversion from Wrapper to float (12.6.4.4) because int is a better conversion target than float (12.6.4.6) because there's an implicit conversion from int to float, but there isn't an implicit conversion from float to int.
To put it another way: int is a sort of "more specific" parameter type than float, in the same way that string would be a "more specific" parameter than object. (Because of the implicit conversions available.)
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.
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 want to overload division operator in my C# class. So, i wrote:
public string[] operator/ (object obj) {
}
And got error: "Parser error: Overloadable unary operator excepted".
So, i cant overload that operator?
On the MSDN i don't see any example: http://msdn.microsoft.com/en-us/library/3b1ff23f.aspx
Thanks.
//i'm using MonoDevelop on Ubuntu 14.10, if it's needed.
You can overload the division operator, but:
It must always be a binary operator - you've only provider one operand
It must always be static
At least one of the operand types must be the type you're declaring it in
So for example:
using System;
class Program
{
public static string operator/ (Program lhs, int rhs)
{
return "I'm divided!";
}
static void Main(string[] args)
{
Console.WriteLine(new Program() / 10);
}
}
The / operator is a "binary" operator, meaning that it takes two arguments: a / b. The way you've written this, it's trying to overload it as a "unary" operator, such as a++.
Something like this ought to work, assuming your class is called "MyObject".
public static string[] operator/ (MyObject mine, object obj) {
...
}
Usage:
object something = "hi";
string[] result = new MyObject(1) / something;