I recently wrote a couple of structs in .NET, to which I then added implicit conversion operators
Example:
public struct Alpha
{
internal string value;
public static implicit operator Alpha(Beta b)
{
return new Alpha() { value = b.value };
}
public static implicit operator Beta(Alpha a)
{
return new Beta() { value = a.value };
}
}
public struct Beta
{
internal string value;
public static implicit operator Alpha(Beta b)
{
return new Alpha() { value = b.value };
}
public static implicit operator Beta(Alpha a)
{
return new Beta() { value = a.value };
}
}
Test:
Alpha a = default(Alpha);
Beta b = a;
// Ambiguous user defined conversions 'Alpha.implicit operator Beta(Alpha)' and 'Beta.implicit operator Beta(Alpha)' when converting from 'Alpha' to 'Beta'
I would like to know what the rules / best practices are surrounding implicit conversion in C#?
Note to self: My gut feeling is that types should not return objects of another type through implicit conversion? i.e. Beta should not return Alpha via implicit conversion, and vice-versa, however, Beta should return Beta, and Alpha should return Alpha
Example (fixed):
public struct Alpha
{
internal string value;
public static implicit operator Alpha(Beta b)
{
return new Alpha() { value = b.value };
}
}
public struct Beta
{
internal string value;
public static implicit operator Beta(Alpha a)
{
return new Beta() { value = a.value };
}
}
Is my assumption correct?
You have a Alpha(Beta x) defined in both classes, thus it is ambiguous which one should be used. Allow each class to handle only the conversion to itself. In other words, struct Alpha implements Alpha(Beta b) because it best knows how to create an instance of itself. Also, I would consider implementing explicit conversions instead of implicit. It can sometimes cause bugs or unexpected conversions accidentally, and when dealing with complex classes are often "lossy" conversions.
public struct Alpha
{
internal string value;
public static implicit operator Alpha(Beta b)
{
return new Alpha() { value = b.value };
}
}
public struct Beta
{
internal string value;
public static implicit operator Beta(Alpha a)
{
return new Beta() { value = a.value };
}
}
The only time you'd likely implement "both ways" in a single class, is if the other class has no knowledge of your new class. This is the more common scenario when you want to support conversion to-from a pre-existing class/struct such as supporting conversion to-from a framework type:
public struct Alpha
{
internal string value;
public static implicit operator Alpha(string b)
{
return new Alpha() { value = b };
}
public static implicit operator string(Alpha a)
{
return a.value;
}
}
No, your assumpion is not correct (even though you wrote working code thanks to it). The problem in your code is that you have defined the same conversion operators in both classes, this is why they are ambiguous; the compiler cannot decide which one to use.
You can read more in section 6.4.3 (Evaluation of user-defined conversions) of the C# 4.0 Language Specification.
Related
I am trying to write an Alias class which enables me to:
int n = new Count(1);
That is, it encapsulates an int in this case as a Count, which gives some type safety and domain meaning, while it automatically converts back to the underlying type.
With non-nullable reference types, I have another issue. I cannot figure out how to handle both of these scenarios at the same time:
int someCount = new Count(1);
Count? nothing = null;
int? noCount = nothing;
This happens because I have types like this:
record Device(Count Cpu, Count? Cores); // Contrived example
Seems like the problem is I cannot overload an operator with both nullable and non-nullable version of the same type:
record Alias<T>(T Value)
{
public static implicit operator T(Alias a) => a.Value;
public static implicit operator T?(Alias? a) => null;
}
record Count : Alias<int> { /**/ }
The point is, if I have a null, I want it converted to null of the target type.
If you don't have any Aliases that wraps reference types, then I think the best thing to do here is to just limit T here to structs. After that, T, and T? become distinct types, allowing you to create two operators:
record Alias<T>(T Value) where T: struct
{
public static implicit operator T?(Alias2<T>? a) => a?.Value;
public static implicit operator T(Alias2<T> a) => a.Value;
}
If you also need to wrap reference types as well, you could consider adding another Alias type that works just for reference types:
record AliasClass<T>(T Value) where T: class
{
[return: NotNullIfNotNull("a")]
public static implicit operator T?(AliasClass<T>? a) => a?.Value;
}
record AliasStruct<T>(T Value) where T: struct
{
public static implicit operator T?(AliasStruct<T>? a) => a?.Value;
public static implicit operator T(AliasStruct<T> a) => a.Value;
}
Then you can have for example:
record Count(int Value) : AliasStruct<int>(Value) { /**/ }
record StringWrapper(string Value) : AliasClass<string>(Value) { /**/ }
As commented, it's not possible to overload an operator with both nullable and non-nullable generic types.
I kind of solved it with extension methods:
public static class AliasClass
{
public static V? Unwrap<V, A>(this Alias<V, A>? a)
where A : Alias<V, A> where V : class => a?.Value;
}
public static class AliasStruct
{
public static V? Unwrap<V, A>(this Alias<V, A>? a)
where A : Alias<V, A> where V : struct => a?.Value;
}
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
public abstract record Alias<V, A> where A : Alias<V, A>
{
protected Alias(V value)
{
Value = value;
EnsureValid(Value);
}
public V Value { get; }
protected virtual void EnsureValid(V value) { }
public override sealed string ToString() => Value?.ToString() ?? "";
public static implicit operator V(Alias<V, A> a) => a.Value;
}
Usage:
int a = new Count(1);
int? n = new Count(2).Unwrap();
The symmetry, sadly, is broken. I can't find a way to implement Unwrap() for the non-nullable case.
I've noticed a difference between the behavior of user-defined implicit conversion to int and user-defined implicit conversion to an arbitrary struct MyStruct when applying operator==.
If I have:
public struct IntA
{
public IntA(int value)
{ m_value = value; }
public static implicit operator int(IntA a)
{ return a.m_value; }
private int m_value;
}
public struct IntB
{
public IntB(int value)
{ m_value = value; }
public static implicit operator int(IntB b)
{ return b.m_value; }
private int m_value;
}
Then the following code compiles:
{
var a = new IntA(3);
var b = new IntB(4);
bool equal = (a == b); // ok! converted to int and used int operator==
// ...
}
This uses my user-defined implicit operator int for IntA and IntB to convert to int, then invoke operator==(int, int).
However, if I have:
public struct MyStruct
{
public MyStruct(int value)
{ m_value = value; }
public static bool operator==(MyStruct lhs, MyStruct rhs)
{ return lhs.m_value == rhs.m_value; }
public static bool operator!=(MyStruct lhs, MyStruct rhs)
{ return lhs.m_value != rhs.m_value; }
private int m_value;
}
public struct MyStructA
{
public MyStructA(int value)
{ m_value = new MyStruct(value); }
public static implicit operator MyStruct(MyStructA a)
{ return a.m_value; }
private MyStruct m_value;
}
public struct MyStructB
{
public MyStructB(int value)
{ m_value = new MyStruct(value); }
public static implicit operator MyStruct(MyStructB b)
{ return b.m_value; }
private MyStruct m_value;
}
Then the following code does not compile:
{
var a = new MyStructA(3);
var b = new MyStructB(4);
bool equal = (a == b); // compile error: Operator `==' cannot be applied to operands of type `MyStructA' and `MyStructB'
// why can't it convert to MyStruct and use that operator==?
// ...
}
I expected it to do the same as the previous example, and use my user-defined implicit operator MyStruct to convert to MyStruct, then invoke operator==(MyStruct, MyStruct).
It doesn't do that. Why not? What's the different between these two cases from the compiler's perspective?
The answer is in the language spec. Emphasis is mine.
C# Language Specification 7.3.4
An operation of the form x op y, where op is an overloadable binary
operator, x is an expression of type X, and y is an expression of type
Y, is processed as follows:
The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of
the union of the candidate operators provided by X and the candidate
operators provided by Y, each determined using the rules of §7.3.5.
If X and Y are the same type, or if X and Y are derived from a common
base type, then shared candidate operators only occur in the combined
set once.
If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator op implementations, including their lifted forms, become the set of candidate operators
for the operation. The predefined implementations of a given
operator are specified in the description of the operator (§7.8
through §7.12). For predefined enum and delegate operators, the
only operators considered are those defined by an enum or delegate
type that is the binding-time type of one of the operands.
The overload resolution rules of §7.5.3 are applied to the set of candidate operators to select the best operator with respect to the
argument list (x, y), and this operator becomes the result of the
overload resolution process. If overload resolution fails to select a
single best operator, a binding-time error occurs.
So, if there's no initial match, it considers all the internally defined == operators as candidates. And since there is one for int, but not MyStruct, you see different behavior.
I'm sure this is a stupid question, but why does the following code not call the explicit operator for the cast on the child class MyBool?
public class DataType
{
public static explicit operator bool(DataType D)
{
return false;
}
public static explicit operator DataType(bool B)
{
return new DataType();
}
}
public class MyBool : DataType
{
public bool Value;
public MyBool()
{
Value = false;
}
public static explicit operator bool(MyBool B)
{
return B.Value;
}
public static explicit operator MyBool(bool B)
{
return new MyBool() { Value = B };
}
}
then:
List<DataType> Types = new List<DataType>();
Types.Add(new MyBool() { Value = true });
Types.Add(new MyBool() { Value = false });
foreach (DataType T in Types)
{
bool Value = (bool)T;
MessageBox.Show(Value.ToString());
}
Produces the output: false, false
Is my only option to write functions on each class to take the place of the explicit operator functions?
why does the following code not call the explicit operator for the cast on the child class MyBool?
Because the operator functions are static, hence also non-virtual and thus their target is resolved at compile time rather than runtime. This is the expected behaviour.
If you want to have polymorphic conversion operators you can call virtual functions inside the operators:
public abstract class DataType
{
public static explicit operator bool(DataType D)
{
return D.DoCastToBool();
}
public static explicit operator DataType(bool B)
{
// We haven’t got an instance of our class here.
// You can use a factory method pattern to emulate virtual constructors.
}
protected abstract bool DoCastToBool();
}
Operators are overloaded rather than overridden - in other words, the choice about which implementation to use is made at compile-time. The compiler only knows about T as DataType, so it calls the operator in DataType.
One option would be to remove the operator from MyBool, but add a virtual method in DataType, allowing for polymorphic behaviour:
public class DataType
{
public static explicit operator bool(DataType D)
{
// TODO: Decide how you want to handle null references
return D.ToBoolean();
}
protected virtual bool ToBoolean()
{
return false;
}
}
public class MyBool : DataType
{
// ...
protected override bool ToBoolean()
{
return Value;
}
}
Note that this won't work for the conversion from bool to a DataType, as in that case we don't have any information about which subtype of DataType you actually want to create.
(Side-note: your code would be easier to follow if you used the normal .NET naming conventions.)
Here's a garbage solution for you:
replace: bool Value = (bool)T;
with: bool Value = (bool)(T as MyBool);
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
}
Apart from the questionable usefulness of this, I'd like to ask if it is possible to do something along these lines.
class MyPrimitive {
String value;
public String Value {
get { return value; }
set { this.value = value; }
}
}
// Instead of doing this...
MyPrimitive a = new MyPrimitive();
a.Value = "test";
String b = a.Value;
// Isn't there a way to do something like this?
MyPrimitive a = "test";
String b = a;
I like to wrap primitive types into custom classes using a property to make the get and set method perform other things, like validation.
Since I'm doing this quite often I thought that it'd be nice to also have a simpler syntax, like for the standard primitives.
Still, I suspect that this not only isn't feasible but could also be conceptually wrong.
Any insights would be most welcome, thanks.
Use a value type (struct) and give it an implicit conversion operator from the type you want on the right hand side of assignment.
struct MyPrimitive
{
private readonly string value;
public MyPrimitive(string value)
{
this.value = value;
}
public string Value { get { return value; } }
public static implicit operator MyPrimitive(string s)
{
return new MyPrimitive(s);
}
public static implicit operator string(MyPrimitive p)
{
return p.Value;
}
}
EDIT: Made the struct immutable because Marc Gravell is absolutely right.
You could use implicit casting. It's not recommended, but:
public static implicit operator string(MyString a) {
return a.Value;
}
public static implicit operator MyString(string a) {
return new MyString { value = a; }
}
Again, bad practice.