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 typeT
only if all of the following are true, whereS0
andT0
are the types that result from removing the trailing?
modifiers, if any, fromS
andT
:
S0
andT0
are different types.Either
S0
orT0
is the class or struct type in which the operator declaration takes place.Neither
S0
norT0
is an interface-type.Excluding user-defined conversions, a conversion does not exist from
S
toT
or fromT
toS
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With