implicit operator using interfaces - c#

I have a generic class that I'm trying to implement implicit type casting for. While it mostly works, it won't work for interface casting. Upon further investigation, I found that there is a compiler error: "User-defined conversion from interface" that applies. While I understand that this should be enforced in some cases, what I'm trying to do does seem like a legitimate case.
Here's an example:
public class Foo<T> where T : IBar
{
private readonly T instance;
public Foo(T instance)
{
this.instance = instance;
}
public T Instance
{
get { return instance; }
}
public static implicit operator Foo<T>(T instance)
{
return new Foo<T>(instance);
}
}
Code to use it:
var concreteReferenceToBar = new ConcreteBar();
IBar intefaceReferenceToBar = concreteReferenceToBar;
Foo<ConcreteBar> concreteFooFromConcreteBar = concreteReferenceToBar;
Foo<IBar> fooFromConcreteBar = concreteReferenceToBar;
Foo<IBar> fooFromInterfaceBar = intefaceReferenceToBar; // doesn't work
Does anyone know a workaround, or can anyone explain in a satisfactory way why I shuouldn't be able to cast interfaceReferenceToBar implicitly to Foo<IBar>, since in my case it is not being converted, but only contained within Foo?
EDIT:
It looks like covariance might offer salvation. Let's hope the C# 4.0 specification allows for implicit casting of interface types using covariance.

The reason you can't do this is because it is specifically forbidden in the C# language specification:
Source: ECMA-334 Section 15.10.4
A class or struct is permitted to
declare a conversion from a source
type S to a target type T provided all
of the following are true:
...
Neither S nor T is object or an interface-type.
and
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

Why my class act so weird with implicit converters [duplicate]

I have a generic class that I'm trying to implement implicit type casting for. While it mostly works, it won't work for interface casting. Upon further investigation, I found that there is a compiler error: "User-defined conversion from interface" that applies. While I understand that this should be enforced in some cases, what I'm trying to do does seem like a legitimate case.
Here's an example:
public class Foo<T> where T : IBar
{
private readonly T instance;
public Foo(T instance)
{
this.instance = instance;
}
public T Instance
{
get { return instance; }
}
public static implicit operator Foo<T>(T instance)
{
return new Foo<T>(instance);
}
}
Code to use it:
var concreteReferenceToBar = new ConcreteBar();
IBar intefaceReferenceToBar = concreteReferenceToBar;
Foo<ConcreteBar> concreteFooFromConcreteBar = concreteReferenceToBar;
Foo<IBar> fooFromConcreteBar = concreteReferenceToBar;
Foo<IBar> fooFromInterfaceBar = intefaceReferenceToBar; // doesn't work
Does anyone know a workaround, or can anyone explain in a satisfactory way why I shuouldn't be able to cast interfaceReferenceToBar implicitly to Foo<IBar>, since in my case it is not being converted, but only contained within Foo?
EDIT:
It looks like covariance might offer salvation. Let's hope the C# 4.0 specification allows for implicit casting of interface types using covariance.
The reason you can't do this is because it is specifically forbidden in the C# language specification:
Source: ECMA-334 Section 15.10.4
A class or struct is permitted to
declare a conversion from a source
type S to a target type T provided all
of the following are true:
...
Neither S nor T is object or an interface-type.
and
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.

Implicit cast from IEnumerable<T> to MyCollection

I'm trying to create an implicit cast that will allow me to use a LINQ result to directly return MyCollection.
public class MyCollection : ICollection<MyType>
{
private List<MyType> _list = new List<MyType>();
public MyCollection(IEnumerable<MyType> collection)
{
_list = new List<MyType>(collection);
}
public static implicit operator MyCollection(IEnumerable<MyType> collection)
{
return new MyCollection(collection);
}
// collection methods excluded for brevity
public MyCollection Filter(string filter)
{
return _list.Where(obj => obj.Filter.Equals(filter)); // cannot implicitly convert
}
}
I've not tried using implicit user-defined casting before, what am I doing wrong?
You aren't allowed to use implicit when either the type cast from, or the type cast to is an interface type. (You also aren't allowed them if one type is derived from the other, which as such bars object as ever being allowed). Indeed, you aren't allowed explicit in this case either. From section §17.9.3 of ECMA-364:
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, where S0 and T0
are the types that result from removing the trailing ? modifiers, if any, from S and T:
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.
You are breaking both the third rule (interface type) and the fourth (because there's a non-user-defined conversion from MyCollection to IEnumerable<MyType> already).
If it were allowed, I'd recommend against it anyway.
Implicit casts should only be used when the effect is utterly obvious (to someone with a reasonable knowledge of the language): It's utterly obvious what long x = 3 + 5 does in casting int to long, and utterly obvious what object x = "abc" does in casting string to object.
Unless your use of implicit is of a similar level of "obvious", then it is a bad idea.
In particular, generally implicit casts should not be implicit in the opposite direction, but rather they should be implicit in one direction (the "widening" direction in most built-in cases) and explicit in the opposite direction (the "narrowing" direction). Since you've already got an implicit cast available from MyCollection to IEnumerable<MyCollection>, having an implicit cast available in the opposite direction is pretty much a bad idea.
More generally, since you are talking about use of Linq, there's an even stronger benefit in using an extensible ToMyCollection() method, because then you are going to be following the Linq convention of ToArray(), ToList(), etc.:
public static class MyCollectionExtensions
{
public static MyCollection ToMyCollection(this IEnumerable<MyType> collection)
{
return collection as MyCollection ?? new MyCollection(collection);
}
}
Note that I test for the case where the collection is already MyCollection to avoid wasteful repeated constructions. You may or may not also want to handle the case of List<MyType> specially in using an internal constructor that assigned it to _list directly.
However, you need to consider the aliasing effects that would allow before doing so. This can be a very useful trick if you know aliasing can't cause problems (either the class is only used internally and the aliasing known not to be an issue, or aliasing doesn't hurt the use of MyCollection, or the aliasing is actually desirable). If in doubt, then just have the method do return new MyCollection(collection) to be safer.
You could use custom extension method instead of implicit conversion:
public static class Extension
{
public static MyCollection ToMyCollection(this IEnumerable<MyType> enumerable)
{
return new MyCollection(enumerable);
}
}
And then use it same as ToList :
return _list.Where(obj => obj.Filter.Equals(filter)).ToMyCollection();
This is the actual line which won't work:
public static implicit operator MyCollection(IEnumerable<MyType> collection)
because what you are trying to do is not allowed:
error CS0552: user-defined conversions to or from an interface are not allowed
You can break too much of CLR internals if redefine interface to class casts, so you are not allowed to.
Best way to proceed is to use custom extension method like #Euphoric advices.
In case you want to extend some functionality of IEnumerable, you can easily use an extenstion:
public static class Extensions
{
public IEnumerable<MyType> Filter(this IEnumerable<MyType> e, string filter)
{
return e.Where(T => T.Filter.Equals(filter));
}
}
That will make it work even without casting operators.
In case you want your implicit casting, this tells you that's not that easy.
Why do you need an ICollection? I would use IEnumerable as in the code below. If you need an ICollection instead, just replace IEnumerable below with ICollection, but be consistent and use one or the other, not a mix of both.
public class MyCollection : IEnumerable<MyType>
{
private IEnumerable<MyType> _list;
public MyCollection(IEnumerable<MyType> collection)
{
_list = collection;
}
// collection methods excluded for brevity
public MyCollection Filter(string filter)
{
return new MyCollection(_list.Where(obj => obj.Filter.Equals(filter)));
}
Please note: if you use ICollection instead of IEnumerable, the Filter command should be:
return new MyCollection(_list.Where(obj => obj.Filter.Equals(filter)).ToList());

How to realize explicit and implicit type conversion for non-generic synonyms of generic struct?

I have a generic struct and two synonyms with defined generic type parameter. I want to define type conversions between these two types like this:
using X = A<int>;
using Y = A<long>;
struct A<T>
{
private T data;
public A(T data)
{
this.data = data;
}
public static explicit operator X(Y value)
{
return new X((int)value.data);
}
public static implicit operator Y(X value)
{
return new Y(value.data);
}
}
and here is an example how I want to use the struct:
class Program
{
static void Main(string[] args)
{
X x = new X(1);
Y y = new Y(2);
x = (X)y; // explicit cast with precision loss
y = x; // implicit cast without precision loss
}
}
Unfortunately each "using" creates new specific definition of the struct and C# compiler treats synonym structures not as a subset of a generic structure, but as an individual structure. That's why compiler reports errors:
"ambigious between X.static implicit operator Y(X) and Y.static implicit operator Y(X)".
"User-defined conversion must convert to or from the enclosing type".
Does anybody knows a way to realize this type conversions without changing type to class?
UPDATE: I'm using .NET 4.
The error
User-defined conversion must convert to or from the enclosing type.
means that your conversion operators need to convert to/from A<T>. Yours are converting to/from A<int/string>. That's not the same thing (much less general at least).
So this cannot work. You have to find some other way to do the conversions. Maybe runtime casting can help here (define the operators as acting on A<T> and do casting inside of them).
I think this problem is unrelated to the type synonyms. In fact they made the question harder to understand.

I can only cast a contravariant delegate with "as"

I'm trying to cast a contravariant delegate but for some reason I can only do it using the "as" operator.
interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);
class MyClass<T> where T : MyInterface
{
public void callDelegate(MyFuncType<MyInterface> func)
{
MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
MyFuncType<T> castFunc2 = func as MyFuncType<T>;
MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
}
}
castFunc2 works fine but castFunc1 and castFunc3 cause the error:
Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'
The MSDN article on the as operator states that castFunc2 and castFunc3 are "equivalent" so I don't understand how only one of them could cause an error. Another piece of this that is confusing me is that changing MyInterface from an interface to a class gets rid of the error.
Can anyone help me understand what is going on here?
Thanks!
Add a constraint such that T must be a class.
class MyClass<T> where T: class, MyInterface
This gives the compiler enough information to know that T is convertible. You don't need the explicit cast either.
Variance only applies to reference types. T is allowed to be a value type without the constraint which breaks the compilers ability to prove that T is compatible for contravariance.
The reason the second statement works is because as actually can perform a null conversion. For example:
class SomeClass { }
interface SomeInterface { }
static void Main(string[] args)
{
SomeClass foo = null;
SomeInterface bar = foo as SomeInterface;
}
Foo is obviously not directly convertable to SomeInterface, but it still succeeds because a null conversion can still take place. Your MSDN reference may be correct for most scenarios, but the generated IL code is very different which means they are fundamentally different from a technical perspective.
Eric Lippert gave a great explanation of this issue in his recent posts: An "is" operator puzzle, part one, An "is" operator puzzle, part two.
Main rationale behind this behavior is following: "is" (or "as") operators are not the same as a cast.
"as" operator can result non-null result event if corresponding cast would be illegal, and this is especially true when we're dealing with type arguments.
Basically, cast operator in your case means (as Eric said) that "I know that this value is of the given type, even though the compiler does not know that, the compiler should allow it" or "I know that this value is not of the given type; generate special-purpose, type-specific code to convert a value of one type to a value of a different type."
Later case deals with value conversions like double-to-integer conversion and we can ignore this meaning in current context.
And generic type arguments are not logical in the first context neither. If you're dealing with a generic type argument, than why you're not stating this "contract" clearly by using generic type argument?
I'm not 100% sure what you're want to achieve, but you can omit special type from your method and freely use generic argument instead:
class MyClass<T> where T : MyInterface
{
public void callDelegate(Action<T> func)
{
}
}
class MyClass2
{
public void callDelegate<T>(Action<T> func)
where T : MyInterface
{
}
}
Otherwise you should use as operator with check for null instead of type check.
Your class says that T implements MyInterface because MyInterface is not a instance type. Therefore, MyFuncType<T> is not guaranteed to be MyFuncType<MyInterface>. It could be MyFuncType<SomeType> and SomeType : MyInterface, but that wouldn't be the same as SomeOtherType : MyInterface. Make sense?

Question about C# 4.0's generics covariance

Having defined this interface:
public interface IInputBoxService<out T> {
bool ShowDialog();
T Result { get; }
}
Why does the following code work:
public class StringInputBoxService : IInputBoxService<string> {
...
}
...
IInputBoxService<object> service = new StringInputBoxService();
and this doesn't?:
public class IntegerInputBoxService : IInputBoxService<int> {
...
}
...
IInputBoxService<object> service = new IntegerInputBoxService();
Does it have anything to do with int being a value type? If yes, how can I circumvent this situation?
Thanks
Yes, it absolutely has to do with int being a value type. Generic variance in C# 4 only works with reference types. This is primarily because references always have the same representation: a reference is just a reference, so the CLR can use the same bits for something it knows is a string reference as for an object reference. The CLR can make sure that the code will be safe, and use native code which only knows about IInputBoxService<object> when passed an IInputBoxService<string> - the value returned from Result will be representationally compatible (if such a term exists!).
With int => object there would have to be boxing etc, so you don't end up with the same code - that basically messes up variance.
EDIT: The C# 4.0 spec says this in section 13.1.3.2:
The purpose of variance annotations is
to provide for more lenient (but still
type safe) conversions to interface
and delegate types. To this end the
definitions of implicit (§6.1) and
explicit conversions (§6.2) make use
of the notion of
variance-convertibility, which is
defined as follows: A type T is variance-convertible to a type
T if T is either an
interface or a delegate type declared
with the variant type parameters T, and for each variant type
parameter Xi one of the following
holds:
Xi is covariant and an
implicit reference or identity
conversion exists from Ai to Bi
Xi
is contravariant and an implicit
reference or identity conversion
exists from Bi to Ai
Xi is invariant
and an identity conversion exists from
Ai to Bi
This doesn't make it terribly obvious, but basically reference conversions only exist between reference types, which leaves only identity conversions (i.e. from a type to itself).
As for workarounds: I think you'd have to create your own wrapper class, basically. This can be as simple as:
public class Wrapper<T>
{
public T Value { get; private set; }
public Wrapper(T value)
{
Value = value;
}
}
It's pretty nasty though :(

Categories

Resources