从IEnumerable 到MyCollection的隐式转换

我正在尝试创建一个隐式转换,允许我使用LINQ结果直接返回MyCollection

 public class MyCollection : ICollection { private List _list = new List(); public MyCollection(IEnumerable collection) { _list = new List(collection); } public static implicit operator MyCollection(IEnumerable 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 } } 

我之前没有尝试使用隐式用户定义的转换,我做错了什么?

当类型转换为或者类型转换为接口类型时,不允许使用implicit式。 (如果一种类型是从另一种类型派生出来的话,也不允许使用它们,因为这样的条形object被允许)。 实际上,在这种情况下你也不被允许explicit 。 根据ECMA-364第17.9.3节:

只有当以下所有条件都为真时,才允许类或结构声明从源类型S到目标类型T的转换,其中S0T0是从删除尾部得到的类型? 来自ST修饰符(如果有):

  • S0T0是不同的类型。

  • S0T0是发生运算符声明的类或结构类型。

  • S0T0都不是接口类型。

  • 排除用户定义的转换,从ST或从TS不存在转换。

您正在破坏第三个规则(接口类型)和第四个规则(因为已经存在从MyCollectionIEnumerable的非用户定义的转换)。

如果它被允许,我建议反对它。

只有当效果完全明显时才能使用隐式强制转换(对于对语言有合理知识的人):在将intlonglong x = 3 + 5 long非常明显,并且完全明显是什么object x = "abc"stringobject

除非你使用implicit的类似“明显”的水平,否则这是一个坏主意。

特别是,通常隐式强制转换不应隐含在相反方向,而应隐含在一个方向(大多数内置情况下的“加宽”方向)和相反方向(“缩小”方向)明确。 由于你已经有一个从MyCollectionIEnumerable的隐式转换,因此在相反的方向上使用隐式转换是一个糟糕的主意。

更一般地说,既然你在谈论Linq的使用,那么使用可扩展的ToMyCollection()方法会有更大的好处,因为那样你就会遵循ToArray()ToList()等的Linq约定:

 public static class MyCollectionExtensions { public static MyCollection ToMyCollection(this IEnumerable collection) { return collection as MyCollection ?? new MyCollection(collection); } } 

请注意,我测试集合已经是MyCollection的情况,以避免浪费重复的构造。 您可能也可能不想处理List的情况,特别是在使用将其直接分配给_list的内部构造函数时。

但是,您需要考虑在执行此操作之前允许的别名效果。 如果您知道别名不会导致问题,这可能是一个非常有用的技巧(该类仅在内部使用,并且已知别名不是问题,或者别名不会影响MyCollection的使用,或者实际上是别名希望的)。 如果有疑问,那么只需让方法return new MyCollection(collection)更安全。

您可以使用自定义扩展方法而不是隐式转换:

 public static class Extension { public static MyCollection ToMyCollection(this IEnumerable enumerable) { return new MyCollection(enumerable); } } 

然后使用它与ToList相同:

 return _list.Where(obj => obj.Filter.Equals(filter)).ToMyCollection(); 

这是不起作用的实际行:

 public static implicit operator MyCollection(IEnumerable collection) 

因为你不想做的事情是 :

错误CS0552:不允许在接口之间进行用户定义的转换

如果重新定义类转换的接口,则可以破坏过多的CLR内部,因此不允许这样做。

最好的方法是使用自定义扩展方法,如@Euphoric建议。

如果您想扩展IEnumerable一些function,您可以轻松使用扩展:

 public static class Extensions { public IEnumerable Filter(this IEnumerable e, string filter) { return e.Where(T => T.Filter.Equals(filter)); } } 

这将使它即使没有铸造操作员也能工作。


如果你想要隐式转换, 这告诉你并不那么容易。

为什么需要ICollection? 我会在下面的代码中使用IEnumerable。 如果你需要一个ICollection,只需用ICollection替换下面的IEnumerable,但要保持一致并使用其中一个,而不是两者兼而有之。

 public class MyCollection : IEnumerable { private IEnumerable _list; public MyCollection(IEnumerable collection) { _list = collection; } // collection methods excluded for brevity public MyCollection Filter(string filter) { return new MyCollection(_list.Where(obj => obj.Filter.Equals(filter))); } 

请注意:如果您使用ICollection而不是IEnumerable,则Filter命令应为:

返回新的MyCollection(_list.Where(obj => obj.Filter.Equals(filter))。ToList());