代码之家  ›  专栏  ›  技术社区  ›  TheCloudlessSky

扩展方法问题的IEnumerable<T>转换

  •  4
  • TheCloudlessSky  · 技术社区  · 15 年前

    我有以下类和扩展类(对于本例):

    public class Person<T>
    {
        public T Value { get; set; }
    }
    
    public static class PersonExt
    {
        public static void Process<TResult>(this Person<IEnumerable<TResult>> p)
        {
            // Do something with .Any().
            Console.WriteLine(p.Value.Any());
        }
    }
    

    我本以为我可以写下下面这样的东西,它会工作,但它没有:

    var x = new Person<List<String>>();
    x.Process();
    

    Person<IEnumerable<String>>

    Person<T> 只要T IEnumerable<Something> .Any() 方法。

    编辑:也许我对协方差的理解是错的?我知道 IEnumerable<String> 应转换为 IEnumerable<Object> IList<String> 转换为 IEnumerable<字符串> ?

    3 回复  |  直到 15 年前
        1
  •  2
  •   Quartermeister    15 年前

    IEnumerable<String> 应该 转换为 IEnumerable<Object> 不能 IList<String> 转换为 IEnumerable<字符串> ?

    IList<字符串> 可以转换为 IEnumerable<字符串> . 问题是你想把 Person<List<String>> Person<IEnumerable<String>> ,这是非法的。例如,它完全可以写:

    var x = new Person<IEnumerable<String>>();
    x.Value = new string[0];
    

    因为值的类型是 IEnumerable<字符串> . 但是,您不能写:

    var x = new Person<List<String>>();
    x.Value = new string[0];
    

    因为值的类型是 List<String> . 既然你不能用 在所有你可以使用 个人<IEnumerable<字符串>&燃气轮机; ,这不是合法演员。

    请注意,如果向扩展方法添加第二个类型参数,则可以执行与所需类似的操作:

    public static void Process<TResult, TList>(this Person<TList> p)
        where TList : IEnumerable<TResult>
    {
        Console.WriteLine(p.Value.Any());
    }
    

    不幸的是,编译器无法同时推断这两个类型参数,因此您必须这样调用它:

    var x = new Person<List<String>>();
    x.Process<String, List<String>>();
    

    接口

    public interface IPerson<out T>
    {
        T Value { get; }
    }
    
    public class Person<T>
        : IPerson<T>
    {
        public T Value { get; set; }
    }
    

    然后将扩展方法写成:

    public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p)
    {
        // Do something with .Any().
        Console.WriteLine(p.Value.Any());
    }
    

    IPerson<T>.Value 是只读的,是 IPerson<List<String>> 可以在任何地方使用 IPerson<IEnumerable<String>> 可以,并且转换有效。

        2
  •  1
  •   Robert Paulson    15 年前

    我不确定你是否掌握了泛型的正确用法。无论如何。。。

    唯一不正确的是您对扩展方法的声明,以及您试图约束扩展方法的方式。

    public static class ThingExtensions
    {
        public static void Process<T>(this Thing<T> p)
            where T : IEnumerable<string>
        {
            // Do something with .Any().
            Console.WriteLine(p.Value.Any());
        }
    }
    

    Person Thing Person<List<string>> 真的是。

    public class Thing<T>
    {
        public T Value { get; set; }
    }
    
    class ListOfString : List<string>
    { }
    
    class Program
    {
        static void Main(string[] args)
        {
            var x = new Thing<ListOfString>();
            x.Value = new ListOfString();
            x.Process();
    
            x.Value.Add("asd");
            x.Process();
    
    
            var x2 = new Thing<int>();
            // Error    1   The type 'int' cannot be used as type parameter 'T' 
            // in the generic type or method 
            // 'ThingExtensions.Process<T>(Thing<T>)'. 
            // There is no boxing conversion from 'int' to 
            // 'System.Collections.Generic.IEnumerable<string>'.    
            //x2.Process();
    
            Console.Read();
        }
    }
    

    Thing<T> 如果这更适用的话。

        3
  •  0
  •   Stephen Cleary    15 年前

    你提到了协方差,但实际上并不使用它。您必须指定 in out 关于你的泛型参数。注意,协方差/逆变对类类型不起作用;它们必须应用于接口。

    public interface IPerson<out T>
    {
      T Value { get; }
    }
    
    public class Person<T> : IPerson<T>
    {
      public T Value { get; set; }
    }
    
    public static class PersonExt
    {
      public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p)
      {
        // Do something with .Any(). 
        Console.WriteLine(p.Value.Any());
      }
    }
    

    var x = new Person<List<String>>();  
    x.Process();