编程语言
首页 > 编程语言> > C#3中具有接口继承(co(ntra) – 方差?)的通用类型推断

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