C#3中具有接口继承(co(ntra) – 方差?)的通用类型推断
作者:互联网
我有以下两种通用类型:
interface IRange<T> where T : IComparable<T>
interface IRange<T, TData> : IRange<T> where T : IComparable<T>
^---------^
|
+- note: inherits from IRange<T>
现在我想为这些接口的集合定义扩展方法,因为它们都是IRange< T>或者来自IRange< T>我希望我能定义一个可以处理两者的方法.注意,该方法不需要处理两者之间的任何差异,只需要处理来自IRange< T>的公共部分.
我的问题是这样的:
我可以定义一个扩展方法来处理这两种类型中的任何一种的集合(IEnumerable< T>)吗?
我试过这个:
public static void Slice<T>(this IEnumerable<IRange<T>> ranges)
where T : IComparable<T>
但是,传递IEnumerable< IRange< Int32,String>>,如下所示:
IEnumerable<IRange<Int32, String>> input = new IRange<Int32, String>[0];
input.Slice();
给我这个编译器错误:
Error 1 ‘System.Collections.Generic.IEnumerable>’ does not contain a definition for ‘Slice’ and no extension method ‘Slice’ accepting a first argument of type ‘System.Collections.Generic.IEnumerable>’ could be found (are you missing a using directive or an assembly reference?) C:\Dev\VS.NET\LVK\LVK.UnitTests\Core\Collections\RangeTests.cs 455 26 LVK.UnitTests
注意:我没想到它会编译.我知道co(ntra)-Variance(有一天我需要知道哪一个是哪种方式)才能知道这是行不通的.我的问题是,我是否可以对Slice声明做任何事情以使其工作.
好的,那么我试着推断范围界面的类型,这样我就可以处理所有类型的IEnumerable< R>只要有问题的R是IRange< T>.
所以我尝试了这个:
public static Boolean Slice<R, T>(this IEnumerable<R> ranges)
where R : IRange<T>
where T : IComparable<T>
这给了我同样的问题.
那么,有没有办法调整这个?
如果没有,我唯一的选择是:
>定义两个扩展方法,并在内部调用内部方法,也许通过将其中一个集合转换为包含基本接口的集合?
>等待C#4.0?
以下是我设想定义这两种方法的方法(注意,我还处于早期设计阶段,所以这可能根本不起作用):
public static void Slice<T>(this IEnumerable<IRange<T>> ranges)
where T : IComparable<T>
{
InternalSlice<T, IRange<T>>(ranges);
}
public static void Slice<T, TData>(this IEnumerable<IRange<T, TData>> ranges)
where T : IComparable<T>
{
InternalSlice<T, IRange<T, TData>>(ranges);
}
private static void Slice<T, R>(this IEnumerable<R> ranges)
where R : IRange<T>
where T : IComparable<T>
这是一个显示我的问题的示例程序代码.
请注意,通过在Main方法中将调用从Slice1更改为Slice2会使两个用法产生编译器错误,因此我的第二次尝试甚至不处理我的初始情况.
using System;
using System.Collections.Generic;
namespace SO1936785
{
interface IRange<T> where T : IComparable<T> { }
interface IRange<T, TData> : IRange<T> where T : IComparable<T> { }
static class Extensions
{
public static void Slice1<T>(this IEnumerable<IRange<T>> ranges)
where T : IComparable<T>
{
}
public static void Slice2<R, T>(this IEnumerable<R> ranges)
where R : IRange<T>
where T : IComparable<T>
{
}
}
class Program
{
static void Main(string[] args)
{
IEnumerable<IRange<Int32>> a = new IRange<Int32>[0];
a.Slice1();
IEnumerable<IRange<Int32, String>> b = new IRange<Int32, String>[0];
b.Slice1(); // doesn't compile, and Slice2 doesn't handle either
}
}
}
解决方法:
我认为你正确地回答了你自己的问题 – 没有C#4.0对接口的共同/逆转支持你不得不写一些重复的代码.
您可能还想使用IEnumerable< T> Enumerable.Cast< T>(此IEnumerable集合)方法 – 执行延迟,因此您可以在代码中使用它(显式)在T和T的子类之间进行转换,而无需创建新集合.
虽然,您可能想要编写自己的强制转换,因为没有约束可以确保集合包含T的后代,因此您对运行时异常持开放态度.我想一个具有以下语法的函数可以工作,但是你将失去混合类型推理和扩展方法的能力:
public static IEnumerable<T> Cast<T,TSubset>(IEnumerable<TSubset> source)
where TSubset : T
{
foreach(T item in source) yield return item;
}
不幸的是,你必须指定T,所以漂亮的干净扩展语法会从窗口中消失(如果有一些约定允许你对扩展方法进行类型推断,并且仍然允许类型参数的显式声明,那么会很好重复可以推断出的类型.
标签:c,collections,inheritance,covariance,contravariance 来源: https://codeday.me/bug/20190701/1343941.html