有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

C语言中的java协方差与逆变#

首先,我要说,我是一名Java开发人员,正在学习用C语言编程。因此,我会将我所知道的与我所学的进行比较

我已经玩了几个小时的C#泛型,我已经能够在C#中重现我在Java中所知道的相同的东西,除了几个使用协方差和逆变的例子。我正在读的那本书在这方面不是很好。我当然会在web上寻找更多信息,但在我这样做的同时,也许您可以帮助我找到以下Java代码的C#实现

一个例子胜过千言万语,我希望通过查看一个好的代码示例,我能够更快地理解这一点

协方差

在Java中,我可以执行以下操作:

public static double sum(List<? extends Number> numbers) {
    double summation = 0.0;
    for(Number number : numbers){
        summation += number.doubleValue();
    }
    return summation;
}

我可以按如下方式使用此代码:

List<Integer> myInts = asList(1,2,3,4,5);
List<Double> myDoubles = asList(3.14, 5.5, 78.9);
List<Long> myLongs = asList(1L, 2L, 3L);

double result = 0.0;
result = sum(myInts);
result = sum(myDoubles)
result = sum(myLongs);

现在我确实发现C#只在接口上支持协方差/逆变,只要它们被显式声明为这样做(out/in)。我想我无法重现这个例子,因为我无法找到所有数字的共同祖先,但我相信如果存在共同祖先,我可以使用IEnumerable实现这样的事情。因为IEnumerable是协变类型。对吧?

关于如何实施上述清单,您有何想法?给我指出正确的方向。是否存在所有数字类型的共同祖先

对冲

我试过的相反的例子如下。在Java中,我可以将一个列表复制到另一个列表中

public static void copy(List<? extends Number> source, List<? super Number> destiny){
    for(Number number : source) {
       destiny.add(number);
    }
}

然后我可以将其用于逆变类型,如下所示:

List<Object> anything = new ArrayList<Object>();
List<Integer> myInts = asList(1,2,3,4,5);
copy(myInts, anything);

我的基本问题是,试图在C#中实现这一点,我找不到一个同时是协变和逆变的接口,就像上面例子中的List一样。也许可以用C#中的两个不同接口实现

有没有关于如何实施的想法

非常感谢大家提供的所有答案。我相信我会从你提供的任何例子中学到很多


共 (4) 个答案

  1. # 1 楼答案

    您可以使用IConvertible接口:

    public static decimal sum<T>(IEnumerable<T> numbers) where T : IConvertible
    {
        decimal summation = 0.0m;
    
        foreach(var number in numbers){
            summation += number.ToDecimal(System.Globalization.CultureInfo.InvariantCulture);
        }
        return summation;
    }
    

    请注意泛型约束(where T : IConvertible),它类似于Java中的extends

  2. # 2 楼答案

    中没有基类Number。网你能得到的最接近的可能是这样的:

    public static double sum(List<object> numbers) {
        double summation = 0.0;
        var parsedNumbers = numbers.Select(n => Convert.ToDouble(n));
        foreach (var parsedNumber in parsedNumbers) {
            summation += parsedNumber;
        }
        return summation;
    }
    

    如果列表中的任何对象不是数字,并且没有实现^{,那么必须捕获在Convert.ToDouble期间发生的任何错误

    更新

    不过,在这种情况下,我个人会使用IEnumerable和泛型类型(而且,多亏了Paul Tyng,您可以强制T实现IConvertible):

    public static double sum<T>(IEnumerable<T> numbers) where T : IConvertible {
        double summation = 0.0;
        var parsedNumbers = numbers.Select(n => Convert.ToDouble(n));
        foreach (var parsedNumber in parsedNumbers) {
            summation += parsedNumber;
        }
        return summation;
    }
    
  3. # 3 楼答案

    对于问题的第二部分,你不需要相反,你只需要声明第一种类型可以转换为第二种类型。同样,使用where TSource: TDest语法来实现这一点。下面是一个完整的示例(展示了如何使用扩展方法):

    static class ListCopy
    {
        public static void ListCopyToEnd<TSource, TDest>(this IList<TSource> sourceList, IList<TDest> destList)
            where TSource : TDest // This lets us cast from TSource to TDest in the method.
        {
            foreach (TSource item in sourceList)
            {
                destList.Add(item);
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            List<int> intList = new List<int> { 1, 2, 3 };
            List<object> objList = new List<object>(); ;
    
            ListCopy.ListCopyToEnd(intList, objList);
            // ListCopyToEnd is an extension method
            // This calls it for a second time on the same objList (copying values again).
            intList.ListCopyToEnd(objList);
    
            foreach (object obj in objList)
            {
                Console.WriteLine(obj);
            }
            Console.ReadLine();
        }
    
  4. # 4 楼答案

    我不会直接回答你们的问题,而是要回答一些稍微不同的问题:

    Does C# have a way to genericize over types that support arithmetic operators?

    不容易,不。如果有能力制作一个Sum<T>方法,可以添加整数、双精度、矩阵、复数、四元数。。。等等虽然这是一个经常被请求的特性,但它也是一个很大的特性,而且它在优先级列表中的位置从来都不足以证明它可以被包含在语言中。我个人很喜欢它,但你不应该期望它出现在C#5中。也许是在一个假设的未来版本的语言

    What is the difference between Java's "call site" covariance/contravariance and C#'s "declaration site" covariance/contravariance?

    实现级别上的根本区别当然是,实际上,Java泛型是通过擦除实现的;虽然您可以获得泛型类型的令人愉快的语法和编译时类型检查的好处,但您不一定会获得C#中的性能好处或运行时类型系统集成好处

    但这实际上更多的是一个实现细节。从我的角度来看,更有趣的区别在于Java的方差规则是在本地执行的,而C的方差规则是在全局执行的

    也就是说:某些变体转换是危险的,因为它们意味着编译器不会捕获某些不安全的操作。经典的例子是:

    • 老虎是哺乳动物
    • X的列表在X中是协变的(假设)
    • 因此,老虎的名单就是哺乳动物的名单
    • 哺乳动物列表中可以插入长颈鹿
    • 因此,你可以在老虎列表中插入长颈鹿

    这显然违反了类型安全,也违反了长颈鹿的安全

    C#和Java使用两种不同的技术来防止这种类型的安全违规。C#说,当I<T>接口被声明时,如果它被声明为协变的,那么接口中一定没有接受T的方法。如果没有方法在列表中插入T,那么你永远不会在老虎列表中插入长颈鹿,因为没有方法插入任何

    相比之下,Java表示,在这个本地站点上,我们可以协变地处理类型,并且我们承诺不会在这里调用任何可能违反类型安全性的方法

    我对Java的特性没有足够的经验来说明在什么情况下哪个“更好”。Java技术当然很有趣