This question already has answers here:
Why isn't it possible to define implicit cast operator from interface to class?
(2 answers)
Closed 9 years ago.
I'm just wondering if anyone knows the reason why you are not allowed to use interfaces with the implicit or explicit operators?
E.g. this raises compile time error:
public static explicit operator MyPlayer(IPlayer player)
{
...
}
"user-defined conversions to or from an interface are not allowed"
Thanks,
Section 10.9.3 of the C# spec spells this out. The short version is that it's disallowed so that the user can be certain that conversions between reference types and interfaces succeed if and only if the reference type actually implements that interface, and that when that conversion takes place that the same object is actually being referenced.
Defining an implicit or explicit conversion between reference types gives the user the expectation that there will be a change in reference; after all, the same reference cannot be both types. On the other hand, the user does not have the same expectation for conversions between reference types and interface types.
User-defined conversions are not allowed to convert from or to interface-types. In particular, this restriction ensures that no user-defined transformations occur when converting to an interface-type, and that a conversion to an interface-type succeeds only if the object being converted actually implements the specified interface-type.
Related
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 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!)
This question already has answers here:
Solution for overloaded operator constraint in .NET generics
(4 answers)
What are generics in C#? [closed]
(3 answers)
Closed 6 years ago.
Generics are used to decouple logic from data type.
public class Calc<T>
{
public T Add(T a, T b)
{
return (a+b);
}
}
But this is throwing the below compile time error
Operator + cannot be applied on type T.
I am not understanding why so. because if it allows from main.cs
main()
{
Calc<int> obj = new Calc<int>();
int c = obj.Add(10,20);
}
Can somebody please explain why I am getting Build errors??
C# generics don't support arbitrary operators. The exact (possibly virtual) method must be known at compile-time of the generic type. Since the generic type argument in your example isn't constrained at all, you can only use the members of the object type, which doesn't include a + operator.
There's no way to use C# generics to do what you're trying to do, sorry. The best you can do is a bunch of type-checks for a few known types (and the appropriate casts, which are tricky with value types), or using reflection (dynamic in particular will work great).
Unrestricted generics like your Calc<T> must be able to compile with any type applied, not all types support the + operand so your code does not compile. This is regardless of what specific types you create your calling code with.
You can restrict the type of T and thus gain access to more methods by doing Calc<T> where T:object or Calc<T> where T: IComparable which would allow you to:
public T CompareTo(T a, T b)
{
return (a.CompareTo(b));
}
Since all T must now implement IComparable. Unfortunately Int32 does not implement any interface which defines the + operator or any addition method. So there is no way to implement that statement you are trying.
C# generics are different from C++ templates. The C# code cannot be compiled if generic type doesn't have definition for used method (operator+ in your case), while C++ compiler applies this method to the actual template argument type.
Addition is not allowed because there is no guarantee that passed types have operator + overload.
There is a way to accomplish that but it will work as long as you pass types that have operator +. Otherwise, RuntimeBinderException will be thrown. Also, there is some impact on performance, but unless code is performance critical, shouldn't be a problem.
public T Add<T>(T a, T b)
{
dynamic da = a, db = b;
return da + db;
}
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.
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.