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.
Related
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'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.
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
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);
}
}
is it possible to do something like the following:
struct test
{
this
{
get { /*do something*/ }
set { /*do something*/ }
}
}
so that if somebody tried to do this,
test tt = new test();
string asd = tt; // intercept this and then return something else
Conceptually, what you want to do here is in fact possible within .NET and C#, but you're barking up the wrong tree with regards to syntax. It seems like an implicit conversion operator would be the solution here,
Example:
struct Foo
{
public static implicit operator string(Foo value)
{
// Return string that represents the given instance.
}
public static implicit operator Foo(string value)
{
// Return instance of type Foo for given string value.
}
}
This allows you to assign and return strings (or any other type) to/from objects of your custom type (Foo here).
var foo = new Foo();
foo = "foobar";
var string = foo; // "foobar"
The two implicit conversion operators don't have to be symmetric of course, though it's usually advisable.
Note: There are also explicit conversion operators, but I think you're more after implicit operators.
You can define implicit and explicit conversion operators to and from your custom type.
public static implicit operator string(test value)
{
return "something else";
}
Expanding on MikeP's answer you want something like:
public static implicit operator Test( string value )
{
//custom conversion routine
}
or
public static explicit operator Test( string value )
{
//custom conversion routine
}