Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit cast from IEnumerable<T> to MyCollection

Tags:

c#

casting

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?

like image 203
Alex Avatar asked Mar 14 '14 10:03

Alex


Video Answer


1 Answers

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.

like image 169
Jon Hanna Avatar answered Sep 19 '22 08:09

Jon Hanna