Implicit conversion to subclass not sufficient? - c#

I'm working with a library that has languages represented by objects of a certain type Language whereas in my code I'm using an enum LanguageEnum from another library that I can't change. I'm trying to build an implicit conversion from the enum to the Lanuage class anyway.
My idea: Create a subclass of Lanuage -- let's say SubLanguage and add and implicit conversion from the language enum to this class.
Although I can do that, it doesn't solve my problem. When i use my LanugageEnum where a Language is expected, the compiler complains that there is no conversion although the compiler can convert to a SubLanguage which should actually be sufficient.
The code looks like this:
public void DoSomethingWith(Language lang) {
}
// somewhere else I call it like this
DoSomethingWith(LanguageEnum.German);
Is it possible to solve my problem without using explicit conversions?

If you want an implicit conversion, you need to specify it exactly in the target type, not its subtypes - otherwise the compiler simply won't consider it.
See the relevant section from the C# specification, 6.4.4 User-defined implicit conversions:
A user-defined implicit conversion from type S to type T is processed as follows:
Determine the types S0 and T0. If S or T are nullable types, S0 and T0 are their underlying types, otherwise S0 and T0 are equal to S and T respectively.
Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), and T0 (if T0 is a class or struct).
Thus, if you are assigning to a variable or field or parameter of type Language, a conversion needs to be declared there, not in SubLanguage.
But, if I may say so, don't fixate on implicit conversions. In the long run, there's nothing wrong with making operations explicit and visible. Especially if they are hugely representation-changing operations.

Related

Why `IEnumerable<int>` is not `IEnumerable<object>`?

IEnumerable<T> interface is covariant, so IEnumerable<string> is IEnumerable<object>.
But why IEnumerable<int> is not IEnumerable<object> while int is object?
Variance conversions like this are only supported if there is an implicit reference or identity conversion between the type parameters. As the spec says:
A type T<Aᵢ, ..., Aᵥ> is variance-convertible to a type T<Bᵢ, ..., Bᵥ> if T is either an interface or a delegate type declared with the variant type parameters T<Xᵢ, ..., Xᵥ>, and for each variant type parameter Xᵢ one of the following holds:
Xᵢ is covariant and an implicit reference or identity conversion exists from Aᵢ to Bᵢ
Xᵢ is contravariant and an implicit reference or identity conversion exists from Bᵢ to Aᵢ
Xᵢ is invariant and an identity conversion exists from Aᵢ to Bᵢ
There is neither a reference not identity conversion between int and object, only a boxing conversion.
You might be wondering what's so special about implicit reference and identity conversions, that they allow variance conversions. Well, these conversions don't require the runtime to do anything at all!
To convert from a string to object, for example, is purely a compile-time matter. Nothing needs to be done to the bits representing the string, in order to turn it into an object. After all, every string is an object.
Because of this, converting from IEnumerable<string> to IEnumerable<object> is trivial too - the runtime does not need to do anything.
This is not true for the int-to-object conversion, however. Because int is a value type and object is a reference type. to maintain these semantics, the runtime needs to create a reference type wrapper around that int in order to do the conversion, "boxing" it.
This means that the runtime cannot just "do nothing" if it wants to convert a IEnumerable<int> to IEnumerable<object>. You would expect to get reference types out of that IEnumerable, not value types, so when and how does the "boxing" happen? When and how should this happen in general, not just in the case of IEnumerable<T>?
I'm sure this in theory can all be figured out, designed, and implemented, but it would certainly not be as trivial as the case with implicit reference conversions. In fact, I reckon it would be a lot more complicated and very drastic changes to both the language and runtime would be needed, which is probably why this is not implemented.

Why implicit conversions don't work in C # [duplicate]

Why will the below not compile? What's special about the interface that causes the compiler to think it can't cast from Container<T> to T, when T is an interface? I don't think its a covariant issue, as I'm not downcasting, but perhaps it is. This is quite like Why C# compiler doesn't call implicit cast operator? but I don't think it's quite the same.
Product pIn =null;
Product pOut;
Container<Product> pContainer;
List<Product> pListIn = null;
List<Product> pListOut;
Container<List<Product>> pListContainer;
IList<Product> pIListIn = null;
IList<Product> pIListOut;
Container<IList<Product>> pIListContainer;
pContainer = pIn;
pOut = pContainer; // all good
pListContainer = pListIn;
pListOut = pListContainer; // all good too
pIListContainer = pIListIn; // fails , cant do implicit cast for some reason
pIListOut = pIListContainer; // and here too
class Container<T>
{
private T value;
private Container(T item) { value = item; }
public static implicit operator Container<T>(T item)
{
return new Container<T>(item);
}
public static implicit operator T(Container<T> container)
{
return container.value;
}
}
Cannot implicitly convert type 'Container<IList<Product>>' to 'IList<Product>'. An explicit conversion exists (are you missing a cast?)
Cannot implicitly convert type 'IList<Product>' to 'Container<IList<Product>>'. An explicit conversion exists (are you missing a cast?)
User defined conversions aren't allowed on interfaces at all. It would potentially be ambiguous, because the type you're trying to convert from could implement the interface itself - at which point what would the cast mean? A reference conversion like a normal cast, or an invocation of the user-defined conversion?
From section 10.3.3 of the C# 4 spec:
For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:
S0 and T0 are different types.
Either S0 or T0 is the class or struct type in which the operator declaration takes place.
Neither S0 nor T0 is an interface-type.
Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.
and then later:
However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions
...
In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. Specifically:
If a pre-defined implicit conversion (§6.1) exists from type S to type T, all user-defined conversions (implicit or explicit) from S to T are ignored.
If a pre-defined explicit conversion (§6.2) exists from type S to type T, any user-defined explicit conversions from S to T are ignored. Furthermore:
If T is an interface type, user-defined implicit conversions from S to T are ignored.
Otherwise, user-defined implicit conversions from S to T are still considered.
Note the first nested bullet here.
(I can thoroughly recommend getting hold of the spec by the way. It's available online in various versions and formats, but the hardcopy annotated edition is also a goldmine of little nuggets from the team and others. I should confess a certain bias here, as I'm one of the annotators - but ignoring my stuff, all the other annotations are well worth reading!)

Ambiguous call between overloads of two-way implicit castable types when a derived type of one is passed as parameter

(Trying to find a title that sums up a problem can be a very daunting task!)
I have the following classes with some overloaded methods that produce a call ambiguity compiler error:
public class MyClass
{
public static void OverloadedMethod(MyClass l) { }
public static void OverloadedMethod(MyCastableClass l) { }
//Try commenting this out separately from the next implicit operator.
//Comment out the resulting offending casts in Test() as well.
public static implicit operator MyCastableClass(MyClass l)
{
return new MyCastableClass();
}
//Try commenting this out separately from the previous implicit operator.
//Comment out the resulting offending casts in Test() as well.
public static implicit operator MyClass(MyCastableClass l)
{
return new MyClass();
}
static void Test()
{
MyDerivedClass derived = new MyDerivedClass();
MyClass class1 = new MyClass();
MyClass class2 = new MyDerivedClass();
MyClass class3 = new MyCastableClass();
MyCastableClass castableClass1 = new MyCastableClass();
MyCastableClass castableClass2 = new MyClass();
MyCastableClass castableClass3 = new MyDerivedClass();
OverloadedMethod(derived); //Ambiguous call between OverloadedMethod(MyClass l) and OverloadedMethod(MyCastableClass l)
OverloadedMethod(class1);
OverloadedMethod(class2);
OverloadedMethod(class3);
OverloadedMethod(castableClass1);
OverloadedMethod(castableClass2);
OverloadedMethod(castableClass3);
}
public class MyDerivedClass : MyClass { }
public class MyCastableClass { }
There are two very interesting things to note:
Commenting out any of the implicit operator methods removes the ambiguity.
Trying to rename the first method overload in VS will rename the first four calls in the Test() method!
This naturally poses two questions:
What it the logic behind the compiler error (i.e. how did the compiler arrive to an ambiguity)?
Is there anything wrong with this design? Intuitively there should be no ambiguity and the offending call should be resolved in the first method overload (OverloadedMethod(MyClass l, MyClass r)) as MyDerivedClass is more closely related to MyClass rather than the castable but otherwise irrelevant MyCastableClass. Furthermore VS refactoring seems to agree with this intuition.
EDIT:
After playing around with VS refactoring, I saw that VS matches the offending method call with the first overload that is defined in code whichever that is. So if we interchange the two overloads VS matches the offending call to the one with the MyCastableClass parameter. The questions are still valid though.
What it the logic behind the compiler error (i.e. how did the compiler arrive to an ambiguity)?
First we must determine what methods are in the method group. Clearly there are two methods in the method group.
Second, we must determine which of those two methods are applicable. That is, every argument is convertible implicitly to the corresponding parameter type. Clearly both methods are applicable.
Third, given that there is more than one applicable method, a unique best method must be determined. In the case where there are only two methods each with only one parameter, the rule is that the conversion from the argument to the parameter type of one must be better than to the other.
The rules for what makes one conversion better than another is in section 7.5.3.5 of the specification, which I quote here for your convenience:
Given a conversion C1 that converts from a type S to a type T1, and a conversion C2 that converts from a type S to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
• An identity conversion exists from S to T1 but not from S to T2
• T1 is a better conversion target than T2
Given two different types T1 and T2, T1 is a better conversion target than T2 if at least one of the following holds:
• An implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists
The purpose of this rule is to determine which type is more specific. If every Banana is a Fruit but not every Fruit is a Banana, then Banana is more specific than Fruit.
• T1 is a signed integral type and T2 is an unsigned integral type.
Run down the list. Is there an identity conversion from MyDerivedClass to either MyCastableClass or MyClass? No. Is there an implicit conversion from MyClass to MyCastableClass but not an implicit conversion going the other way? No. There is no reason to suppose that either type is more specific than the other. Are either integral types? No.
Therefore there is nothing upon which to base the decision that one is better than the other, and therefore this is ambiguous.
Is there anything wrong with this design?
The question answers itself. You've found one of the problems.
Intuitively there should be no ambiguity and the offending call should be resolved in the first method overload as MyDerivedClass is more closely related to MyClass
Though that might be intuitive to you, the spec does not make a distinction in this case between a user-defined conversion and any other implicit conversion. However I note that your distiction does count in some rare cases; see my article for details. (Chained user-defined explicit conversions in C#)
What it the logic behind the compiler error?
Well, the compiler determines the signature based on a few things. The number and type of the parameters is next to the name, one of the most important ones. The compiler checks if a method call is ambiguous. It doesn't only use the actual type of the parameter, but also the types it can be implicitly casted to (note that explicit casts are out of the picture, they are not used here).
This gives the issue you describe.
Is there anything wrong with this design?
Yes. Ambiguous methods are a source of a lot of problems. Especially when using variable types, like dynamic. Even in this case, the compiler can't choose which method to call, and that is bad. We want software to be deterministic, and with this code, it can't be.
You didn't ask for it, but I guess the best option is:
To rethink your design. Do you really need the implicit casts? If so, why do you need two methods instead of one?
Use explicit casting instead of implicit casting, to make casting a deliberate choice a compiler can understand.

Converting between types

I'm studying for the 70-536 exam and now I'm checking the lesson about converting between types and I have doubts.
Always implicit conversion it's a widening conversion? and Explicit conversion it's a narrowing conversion?
Also this is consider an Narrowing conversion?
System.Int32 number32 = 25548612
System.Int16 number16 = (System.Int16) number32;
That narrowing conversions should be explicit, and widening conversions may be implicitly is only a design guideline. It is possible to create conversions that violate this guideline with user defined conversions. It's also possible to use an explicit conversion whenever the types implemented the implicit conversion.
A widening conversion is a conversion where every value of the original type can be represented in the result type.
A narrowing conversion is a conversion where some values of the original type cannot be represented in the result type.
Since some values of Int32 cannot be represented as Int16, this is a narrowing conversion. Depending on the compiler options, values outside the range of Int16 either overflow, or throw an exception.
Contrary to what I previously said, this concept also applies to conversions between base- and derived classes.
You need to thing of types as sets of possible values. And not about which members they have.
Every instance of the derived class is always a valid value of a variable of the base class. So casting from the derived class to the base class is widening, and thus implicit.
Some instances of the base class are not valid values of the derived class(For example they derive from a different subtree, or are instances of the base class itself). So casting from the base class to the derived class is narrowing, and thus explicit.
There are some implicit conversions, that are only widening in a loose sense, where the conversion is lossy.
In particular int/Int32 to float/Single and long/Int64 to double/Double. With these conversions some input values can only be represented approximately in the result type.
You need to look at types as a set of allowed values. Then you see that every instance of the derived class, is also an allowed value for the base class. Thus the conversion from derived to base class is widening.
And conversely there are values of the base class, that are not legal values of the derived class.
You can turn it around:
a narrowing conversion will always be explicit
a widening conversion will be implicit.
Assuming a sane implementation of course.
What might help you more: an implicit conversion should always be 'safe' in the sense that it will not throw an exception. An explicit exception might protest.
You can rely on this for the built-in conversions. For custom conversions these are only guidelines, they can be broken.
Always implicit conversion it's widening conversion???
No. Keep in mind that you can define your own implicit conversions. You can make them widening or not, if you want.
Explicit conversion it's a narrowing conversion??
No, same reasoning.
Also this is consider an Narrowing conversion?
Yes. There's clearly a loss of information.

What is the difference between casting and coercing?

I've seen both terms be used almost interchangeably in various online explanations, and most text books I've consulted are also not entirely clear about the distinction.
Is there perhaps a clear and simple way of explaining the difference that you guys know of?
Type conversion (also sometimes known as type cast)
To use a value of one type in a context that expects another.
Nonconverting type cast (sometimes known as type pun)
A change that does not alter the underlying bits.
Coercion
Process by which a compiler automatically converts a value of one type into a value of another type when that second type is required by the surrounding context.
Type Conversion:
The word conversion refers to either implicitly or explicitly changing a value from one data type to another, e.g. a 16-bit integer to a 32-bit integer.
The word coercion is used to denote an implicit conversion.
The word cast typically refers to an explicit type conversion (as opposed to an implicit conversion), regardless of whether this is a re-interpretation of a bit-pattern or a real conversion.
So, coercion is implicit, cast is explicit, and conversion is any of them.
Few examples (from the same source) :
Coercion (implicit):
double d;
int i;
if (d > i) d = i;
Cast (explicit):
double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9
Usages vary, as you note.
My personal usages are:
A "cast" is the usage of a cast operator. A cast operator instructs the compiler that either (1) this expression is not known to be of the given type, but I promise you that the value will be of that type at runtime; the compiler is to treat the expression as being of the given type, and the runtime will produce an error if it is not, or (2) the expression is of a different type entirely, but there is a well-known way to associate instances of the expression's type with instances of the cast-to type. The compiler is instructed to generate code that performs the conversion. The attentive reader will note that these are opposites, which I think is a neat trick.
A "conversion" is an operation by which a value of one type is treated as a value of another type -- usually a different type, though an "identity conversion" is still a conversion, technically speaking. The conversion may be "representation changing", like int to double, or it might be "representation preserving" like string to object. Conversions may be "implicit", which do not require a cast, or "explicit", which do require a cast.
A "coercion" is a representation-changing implicit conversion.
Casting is the process by which you treat an object type as another type, Coercing is converting one object to another.
Note that in the former process there is no conversion involved, you have a type that you would like to treat as another, say for example, you have 3 different objects that inherit from a base type, and you have a method that will take that base type, at any point, if you know the specific child type, you can CAST it to what it is and use all the specific methods and properties of that object and that will not create a new instance of the object.
On the other hand, coercing implies the creation of a new object in memory of the new type and then the original type would be copied over to the new one, leaving both objects in memory (until the Garbage Collectors takes either away, or both).
As an example consider the following code:
class baseClass {}
class childClass : baseClass {}
class otherClass {}
public void doSomethingWithBase(baseClass item) {}
public void mainMethod()
{
var obj1 = new baseClass();
var obj2 = new childClass();
var obj3 = new otherClass();
doSomethingWithBase(obj1); //not a problem, obj1 is already of type baseClass
doSomethingWithBase(obj2); //not a problem, obj2 is implicitly casted to baseClass
doSomethingWithBase(obj3); //won't compile without additional code
}
obj1 is passed without any casting or coercing (conversion) because it's already of the same type baseClass
obj2 is implicitly casted to base, meaning there's no creation of a new object because obj2 can already be baseClass
obj3 needs to be converted somehow to base, you'll need to provide your own method to convert from otherClass to baseClass, which will involve creating a new object of type baseClass and filling it by copying the data from obj3.
A good example is the Convert C# class where it provides custom code to convert among different types.
According to Wikipedia,
In computer science, type conversion, type casting, type coercion, and type juggling are different ways of changing an expression from one data type to another.
The difference between type casting and type coercion is as follows:
TYPE CASTING | TYPE COERCION
|
1. Explicit i.e., done by user | 1. Implicit i.e., done by the compiler
|
2. Types: | 2. Type:
Static (done at compile time) | Widening (conversion to higher data
| type)
Dynamic (done at run time) | Narrowing (conversion to lower data
| type)
|
3. Casting never changes the | 3. Coercion can result in representation
the actual type of object | as well as type change.
nor representation. |
Note: Casting is not conversion. It is just the process by which we treat an object type as another type. Therefore, the actual type of object, as well as the representation, is not changed during casting.
I agree with #PedroC88's words:
On the other hand, coercing implies the creation of a new object in
memory of the new type and then the original type would be copied over
to the new one, leaving both objects in memory (until the Garbage
Collectors takes either away, or both).
Casting preserves the type of objects. Coercion does not.
Coercion is taking the value of a type that is NOT assignment compatible and converting to a type that is assignment compatible. Here I perform a coercion because Int32 does NOT inherit from Int64...so it's NOT assignment compatible. This is a widening coercion (no data lost). A widening coercion is a.k.a. an implicit conversion. A Coercion performs a conversion.
void Main()
{
System.Int32 a = 100;
System.Int64 b = a;
b.GetType();//The type is System.Int64.
}
Casting allows you to treat a type as if it were of a different type while also preserving the type.
void Main()
{
Derived d = new Derived();
Base bb = d;
//b.N();//INVALID. Calls to the type Derived are not possible because bb is of type Base
bb.GetType();//The type is Derived. bb is still of type Derived despite not being able to call members of Test
}
class Base
{
public void M() {}
}
class Derived: Base
{
public void N() {}
}
Source: The Common Language Infrastructure Annotated Standard by James S. Miller
Now what's odd is that Microsoft's documentation on Casting does not align with the ecma-335 specification definition of Casting.
Explicit conversions (casts): Explicit conversions require a cast
operator. Casting is required when information might be lost in the
conversion, or when the conversion might not succeed for other
reasons. Typical examples include numeric conversion to a type that
has less precision or a smaller range, and conversion of a base-class
instance to a derived class.
...This sounds like Coercions not Casting.
For example,
object o = 1;
int i = (int)o;//Explicit conversions require a cast operator
i.GetType();//The type has been explicitly converted to System.Int32. Object type is not preserved. This meets the definition of Coercion not casting.
Who knows? Maybe Microsoft is checking if anybody reads this stuff.
From the CLI standard:
I.8.3.2 Coercion
Sometimes it is desirable to take a value of a type that is not assignable-to a location, and convert
the value to a type that is assignable-to the type of the location. This is accomplished through
coercion of the value. Coercion takes a value of a particular type and a desired type and attempts
to create a value of the desired type that has equivalent meaning to the original value. Coercion
can result in representation change as well as type change; hence coercion does not necessarily
preserve object identity.
There are two kinds of coercion: widening, which never loses information, and narrowing, in
which information might be lost. An example of a widening coercion would be coercing a value
that is a 32-bit signed integer to a value that is a 64-bit signed integer. An example of a
narrowing coercion is the reverse: coercing a 64-bit signed integer to a 32-bit signed integer.
Programming languages often implement widening coercions as implicit conversions, whereas
narrowing coercions usually require an explicit conversion.
Some coercion is built directly into the VES operations on the built-in types (see §I.12.1). All
other coercion shall be explicitly requested. For the built-in types, the CTS provides operations
to perform widening coercions with no runtime checks and narrowing coercions with runtime
checks or truncation, according to the operation semantics.
I.8.3.3 Casting
Since a value can be of more than one type, a use of the value needs to clearly identify which of
its types is being used. Since values are read from locations that are typed, the type of the value
which is used is the type of the location from which the value was read. If a different type is to
be used, the value is cast to one of its other types. Casting is usually a compile time operation,
but if the compiler cannot statically know that the value is of the target type, a runtime cast check
is done. Unlike coercion, a cast never changes the actual type of an object nor does it change the
representation. Casting preserves the identity of objects.
For example, a runtime check might be needed when casting a value read from a location that is
typed as holding a value of a particular interface. Since an interface is an incomplete description
of the value, casting that value to be of a different interface type will usually result in a runtime
cast check.
Below is a posting from the following article:
The difference between coercion and casting is often neglected. I can see why; many languages have the same (or similar) syntax and terminology for both operations. Some languages may even refer to any conversion as “casting,” but the following explanation refers to concepts in the CTS.
If you are trying to assign a value of some type to a location of a different type, you can generate a value of the new type that has a similar meaning to the original. This is coercion. Coercion lets you use the new type by creating a new value that in some way resembles the original. Some coercions may discard data (e.g. converting the int 0x12345678 to the short 0x5678), while others may not (e.g. converting the int 0x00000008 to the short 0x0008, or the long 0x0000000000000008).
Recall that values can have multiple types. If your situation is slightly different, and you only want to select a different one of the value’s types, casting is the tool for the job. Casting simply indicates that you wish to operate on a particular type that a value includes.
The difference at the code level varies from C# to IL. In C#, both casting and coercion look fairly similar:
static void ChangeTypes(int number, System.IO.Stream stream)
{
long longNumber = number;
short shortNumber = (short)number;
IDisposable disposableStream = stream;
System.IO.FileStream fileStream = (System.IO.FileStream)stream;
}
At the IL level they are quite different:
ldarg.0
conv.i8
stloc.0
ldarg.0
conv.i2
stloc.1
ldarg.1
stloc.2
ldarg.1
castclass [mscorlib]System.IO.FileStream
stloc.3
As for the logical level, there are some important differences. What’s most important to remember is that coercion creates a new value, while casting does not. The identity of the original value and the value after casting are the same, while the identity of a coerced value differs from the original value; coersion creates a new, distinct instance, while casting does not. A corollary is that the result of casting and the original will always be equivalent (both in identity and equality), but a coerced value may or may not be equal to the original, and never shares the original identity.
It’s easy to see the implications of coercion in the examples above, as the numeric types are always copied by value. Things get a bit trickier when you’re working with reference types.
class Name : Tuple<string, string>
{
public Name(string first, string last)
: base(first, last)
{
}
public static implicit operator string[](Name name)
{
return new string[] { name.Item1, name.Item2 };
}
}
In the example below, one conversion is a cast, while the other is a coercion.
Tuple<string, string> tuple = name;
string[] strings = name;
After these conversions, tuple and name are equal, but strings is not equal to either of them. You could make the situation slightly better (or slightly more confusing) by implementing Equals() and operator ==() on the Name class to compare a Name and a string[]. These operators would “fix” the comparison issue, but you would still have two separate instances; any modification to strings would not be reflected in name or tuple, while changes to either one of name or tuple would be reflected in name and tuple, but not in strings.
Although the example above was meant to illustrate some differences between casting and coercion, it also serves as a great example of why you should be extremely cautious about using conversion operators with reference types in C#.

Categories

Resources