Implicit operators and a compiler error - c#

I have a class (simplified for the purposes of this question) that wraps a decimal value and uses a couple of implicit operator declarations for converting between the type and the wrapped value:
private class DecimalWrapper
{
public decimal Value { get; private set; }
public DecimalWrapper(decimal value)
{
Value = value;
}
public static implicit operator decimal(DecimalWrapper a)
{
return a != null ? a.Value : default(decimal);
}
public static implicit operator DecimalWrapper(decimal value)
{
return new DecimalWrapper(value);
}
}
The usages of implicit operator here allow things like these to be done:
DecimalWrapper d1 = 5; // uses implicit operator DecimalWrapper
DecimalWrapper d2 = 10;
var result = d1 * d2; // uses implicit operator decimal
Assert.IsTrue(result.Equals(50));
Assert.IsTrue(result == 50);
Now, consider a second class (again, simplified) that has an overloaded constructor that can take a decimal or a DecimalWrapper:
private class Total
{
private readonly DecimalWrapper _total;
public Total(DecimalWrapper total)
{
_total = total;
}
public Total(decimal totalValue)
{
_total = totalValue;
}
}
I would expect to be able to instantiate an instance of Total by passing in an integer value, which would get converted to a decimal:
var total = new Total(5);
However, this results in a compiler error:
The call is ambiguous between the following methods or properties: 'Namespace.Total.Total(TypeTests.DecimalWrapper)' and 'Namespace.Total.Total(decimal)'
To fix this, you have to remove the implicit operator decimal or specify that the value 5 is in fact a decimal:
var total = new Total(5m);
This is all well and good, but I don't see why the implicit operator decimal is relevant here. So, what is going on?

Are you looking for a citation from the language specification?
The cause of this has to do with overload resolution. When you specify an int value as the constructor parameter, no overload is considered "best" because both require a conversion. The specification doesn't consider two levels of conversion different from one level, so the two constructor overloads are equivalent to each other.
As Blorgbeard noted in the comments, you can easily resolve the issue by getting rid of one of the constructors. He suggests removing the DecimalWrapper overload, but since your field is of the DecimalWrapper type, I'd get rid of the decimal overload instead. Doing it this way, if you specify an int value for the constructor, the compiler will implicitly convert to decimal and then DecimalWrapper for you. If you specify a decimal value for the constructor, the compiler will implicity convert to DecimalWrapper for that call, which is what your decimal constructor would have done anyway.
Of course, yet another way to address the issue would be to add other constructors to the Total class, e.g. one that takes an int. Then no conversion is required and the int constructor would be chosen. But this seems like overkill to me.

Related

32 bit implicit conversions fail generic overload resolution

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.

Clamping and rounding a value during implicit conversion

I've developer a custom integral type. Here it is its definition in C#.
public struct PitchClass
{
private readonly int value;
private PitchClass(int value)
{
this.value = CanonicalModulus.Calculate(value, 12);
}
public static implicit operator PitchClass(int value)
{
return new PitchClass(value);
}
public static implicit operator PitchClass(double value)
{
return new PitchClass((int)Math.Round(value));
}
public static implicit operator int(PitchClass pitchClass)
{
return pitchClass.value;
}
}
The PitchClass is an int whose values are in the range [0, 11].
As you can read from the C# code both int and double values can be implicitly converted to PitchClass using a canonical modulus operator:
PitchClass pitchClass = -3;
Console.WriteLine(pitchClass); // 9
The double value is also rounded during implicit conversion:
PitchClass pitchClass = -3.4d;
Console.WriteLine(pitchClass); // 9
I couldn't find other examples of custom data types that do so many things to the data type to convert.
Why? Is it bad practice? If so, is there another way to avoid doing argument validation for every PitchClass variable in every method?
Thanks
It is not bad practice to create a base type and make it convertible to other base data types. Neither is it to define implicit and explicit conversions.
Look at the implementation of Int32 in the .Net Framework. This structure implements many interfaces to make it Convertible to other structure types, to format it nicely and a few other stuff.
If you intend on heavily using this structure, implementing IConvertible, IComparable, IEquatable (and the methods GetHashCode() & Equals()) is a good idea, because almost all the native data types do so.
The Complex type is given as example of Custom data type in the explanation for IConvertible interface and they do many different conversions to and from other types.
Also, the explicit conversion from double to int is doing kind of the same thing you do, making this conversion narrowing (may incur data loss).

Why do we need to type cast an enum in C#

I have an enum like:
public enum Test:int
{
A=1,
B=2
}
So here I know my enum is an int type but if I want to do something like following:
int a = Test.A;
this doesn't work.
If I have a class like:
public class MyTest
{
public static int A =1;
}
I can say ,
int a = MyTest.A;
Here I don't need to cast A to int explicitly.
So here I know my enum is an int type
No, it's not. It has an underlying type of int, but it's a separate type. Heck, that's half the point of having enums in the first place - that you can keep the types separate.
When you want to convert between an enum value and its numeric equivalent, you cast - it's not that painful, and it keeps your code cleaner in terms of type safety. Basically it's one of those things where the rarity of it being the right thing to do makes it appropriate to make it explicit.
EDIT: One oddity that you should be aware of is that there is an implicit conversion from the constant value 0 to the enum type:
Test foo = 0;
In fact, in the MS implementation, it can be any kind of constant 0:
Test surprise = 0.0;
That's a bug, but one which it's too late to fix :)
I believe the rest for this implicit conversion was to make it simpler to check whether any bits are set in a flags enum, and other comparisons which would use "the 0 value". Personally I'm not a fan of that decision, but it's worth at least being aware of it.
"The underlying type specifies how much storage is allocated for each enumerator. However, an explicit cast is needed to convert from enum type to an integral type".
With your updated example:
public class MyTest
{
public static int A =1;
}
And usage:
int a = MyTest.A;
That's not how enums look. Enums look more like (comments are places where we differ from a real enum):
public struct MyTest /* Of course, this isn't correct, because we'll inherit from System.ValueType. An enum should inherit from System.Enum */
{
private int _value; /* Should be marked to be treated specially */
private MyTest(int value) /* Doesn't need to exist, since there's some CLR fiddling */
{
_value = value;
}
public static explicit operator int(MyTest value) /* CLR provides conversions automatically */
{
return value._value;
}
public static explicit operator MyTest(int value) /* CLR provides conversions automatically */
{
return new MyTest(value);
}
public static readonly MyTest A = new MyTest(1); /* Should be const, not readonly, but we can't do a const of a custom type in C#. Also, is magically implicitly converted without calling a constructor */
public static readonly MyTest B = new MyTest(2); /* Ditto */
}
Yes, you can easily get to the "underlying" int value, but the values of A and B are still strongly typed as being of type MyTest. This makes sure you don't accidentally use them in places where they're not appropriate.
The enum values are not of int type. int is the base type of the enum. The enums are technically ints but logically (from the perspective of the C# language) not. int (System.Int32) is the base type of all enums by default, if you don't explicitly specify another one.
You enum is of type Test. It is not int just because your enum has integers values.
You can cast your enum to get the int value:
int a = (int) Test.A;

overload operator = in C#. How can i accept other types?

So a friend was telling me how a game was hacked and how the technique worked. He then asked whats the best way to prevent that kind of attack. The most straight forward way i knew was to A) the shuffle the bits of important value B) hash the values and compare them every time (an int that holds the score or money is likely to be checked rarely).
Then i tried the implementation in C#, i couldnt overload the = operator. How can i do this?
ex code.
class EncryptVal <T>
{
T v;
public T operator = (T v2)
{
//shuffle bits
}
public T operator ()()
{
//return unshuffle bits
}
}
You're looking for the implicit and explicit operator, rather than saying =. This allows you to define how things will work when cast implicitly (ie, just an assignment) and explicitly (ie, there's a casting operator).
public static implicit operator Type1(Type2 p) {}
public static explicit operator Type1(Type2 p) {}
You can encapsulate the value in the class and overload the implicit conversions to and from the class:
public class EncryptVal<T> {
private T _value;
private EncryptVal(T value) {
_value = value;
}
public static implicit operator EncryptVal<T>(T value) {
//shuffle bits
return new EncryptVal<T>(value);
}
public static implicit operator T(EncryptVal<T> value) {
//unshuffle bits
return value._value;
}
}
Usage:
// implicit conversion from int
EncryptVal<int> e = 42;
// implicit conversion to int
int i = e;
You are not allowed to overload the assignment operator in C#. Here's the MSDN documentation on it.
You'll have to create your own function for this behavior.
I assume that you come from C++ where it is very common to write classes that are used like primitive data types. In C# you do things more explicitly.
I would write it as a property or as two methods, eg:
class EncryptVal <T>
{
T v;
public T Value
{
get
{
//return unshuffle bits
}
set
{
//shuffle bits
}
}
}
Dont use = for setting the value. You cant overload assignment.
What you can do is hide it behind a property.
int _encyptedValue;
Public int myInt
{
get
{
return Decrypt(_encryptedValue);
}
set
{
_encryptedValue = Encrypt(value);
}
}
You get to chosse your decryption/encryption
I would go the for implicit/explicit operator overloading for the implementation part.
Probably the explicit one since your conversion does heavy processing, and that it eventually could fail.
I would just add that shuffling bits seems to be only an obfuscation technic that will surely not last long if you have wishfull hackers interested in your game.
You probably need stronger cryptography to protect your data, but more context is needed.

typedef equivalent for overloading in c#

I have a bunch of code that has lots integers with different meanings (I'd rather a general solution but for a specific example: day-of-the-month vs. month-of-the-year vs. year etc.). I want to be able to overload a class constructor based on these meanings.
For example
int a; // takes role A
int b; // takes role B
var A = new Foo(a); // should call one constructor
var B = new Foo(b); // should call another constructor
Now clearly that won't work but if I could define a type (not just an alias) that is an int in all but name like this:
typedef int TypeA; // stealing the C syntax
typedef int TypeB;
I could do the overloading I need and let the type system keep track of what things are what. In particular this would allow me to be sure that values are not mixed up, for example a value returned from a function as a year is not used as a day-of-the-month.
Is there any way short of class or struct wrappers to do this in c#?
It would be nice if the solution would also work for floats and doubles.
There is no direct typedef equivalent, but you can do the following:
using TypeA = int;
using TypeB = int;
However, this just aliases the type rather than creating a new strong type. Therefore, the compiler will still treat them as an int when resolving method calls.
A better solution might be to create simple wrapper classes that wraps int and provides implicit casting, such as:
struct TypeA
{
public TypeA(int value)
{
this.realValue = value;
}
private int realValue;
public static implicit operator int(TypeA value)
{
return this.realValue;
}
public static implicit operator TypeA(int value)
{
return new TypeA(value);
}
}
However, in most situations, an enum would be more appropriate.
This may be way off, but couldnt you use an enum for this? Enum base is int, but is typed, and you could define different constructors based on the type of enum passed.
If it's just for the constructor, couldn't you use something like this:
int dom = 5;
int doy = 100;
Foo b = Foo.NewFromDayoftheMonth(dom);
Foo c = Foo.NewFromDayoftheYear(doy);
where each method are static and create a new Foo object based on the parameters passed in.
This seems like a solution to me, where there isn't much else of one.
I would also create a struct around the value :
public struct TypeA
{
private int value;
public TypeA(int value)
{
this.value = value;
}
public static explicit operator int(TypeA a)
{
return a.value;
}
public static explicit operator TypeA(int value)
{
return new TypeA(value);
}
}
you can also declare operators to combine types and methods to provide a rich type around the int value.
A Year type can provde a IsLeap property, a day of month can be restraint to values between 1 and 31 and provide a special arithmetic.

Categories