简体   繁体   English

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

[英]Conversion of IEnumerable<T> for Extension Method Issue

I have the following class and extension class (for this example): 我有以下类和扩展类(对于此示例):

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());
    }
}

I was expecting I could write something like the following and it would work, but it doesn't: 我期待我可以编写类似下面的东西,它会起作用,但它不会:

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

Since List is lower in the inheritance tree than IEnumerable, shouldn't this be valid? 由于List在继承树中比IEnumerable低,这不应该有效吗? It works if I new up a Person<IEnumerable<String>> of course because that's the direct type. 如果我新建一个Person<IEnumerable<String>> ,那当然是因为那是直接类型。

I'm trying to use an extension method that can be applied to all Person<T> 's as long as T implements IEnumerable<Something> because I need to use the .Any() method. 我正在尝试使用可以应用于所有Person<T>的扩展方法,只要T实现IEnumerable<Something>因为我需要使用.Any()方法。

EDIT: Maybe my understanding of covariance is off? 编辑:也许我对协方差的理解是关闭的? I know IEnumerable<String> should convert to IEnumerable<Object> , but couldn't IList<String> convert to IEnumerable<String> ? 我知道IEnumerable<String>应该转换为IEnumerable<Object> ,但是无法将IList<String>转换为IEnumerable<String>

EDIT2: Forgot to mention that I am using .net 4.0. 编辑2:忘了提到我正在使用.net 4.0。

I know IEnumerable<String> should convert to IEnumerable<Object> , but couldn't IList<String> convert to IEnumerable<String> ? 我知道IEnumerable<String>应该转换为IEnumerable<Object> ,但是无法将IList<String>转换为IEnumerable<String>

IList<String> can convert to IEnumerable<String> . IList<String>可以转换为IEnumerable<String> The problem is that you're trying to convert Person<List<String>> to Person<IEnumerable<String>> , which is illegal. 问题是你试图将Person<List<String>>转换为Person<IEnumerable<String>> ,这是非法的。 For example, it's perfectly valid to write: 例如,写下来是完全有效的:

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

since Value is of type IEnumerable<String> and a string array is an IEnumerable<String> . 因为Value的类型为IEnumerable<String>而字符串数组是IEnumerable<String> However, you cannot write: 但是,你不能写:

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

since Value is of type List<String> . 因为Value的类型为List<String> Since you can't use a Person<List<String>> in all places where you could use a Person<IEnumerable<String>> , it's not a legal cast. 由于您不能在所有可以使用Person<IEnumerable<String>>地方使用Person<List<String>> Person<IEnumerable<String>> ,因此它不是合法的演员。

Note that you can do something similar to what you want if you add a second type parameter to your extension method: 请注意,如果向扩展方法添加第二个类型参数,则可以执行与所需操作类似的操作:

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

Unfortunately, the compiler won't be able to infer both type parameters, so you would have to call it like this: 不幸的是,编译器无法推断出两个类型参数,所以你必须像这样调用它:

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

If you are using C# 4.0 and can use covariance, then you can define a covariant interface for person: 如果您使用的是C#4.0并且可以使用协方差,那么您可以为person定义协变接口

public interface IPerson<out T>
{
    T Value { get; }
}

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

And then write your extension method as: 然后将您的扩展方法编写为:

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

Since IPerson<T>.Value is read-only, a IPerson<List<String>> can be used everywhere that an IPerson<IEnumerable<String>> can be, and the conversion is valid. 由于IPerson<T>.Value是只读的, IPerson<List<String>>可以在IPerson<IEnumerable<String>>可以使用的任何地方使用IPerson<List<String>> ,并且转换有效。

I'm not sure you've quite grasped the correct use of generics. 我不确定你是否已经掌握了正确使用仿制药的方法。 In any event ... 在任何情况下 ...

The only thing that is incorrect is your declaration of extension method, and the way you are attempting to constrain the extension method. 唯一不正确的是您的扩展方法声明,以及您尝试约束扩展方法的方式。

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());
    }
}

All I've really done is rename Person to Thing so that we're not getting hung up on what a Person<List<string>> really is. 所有我真正做的就是将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();
    }
}

You could also move the generic constraint to the Thing<T> if that was more applicable. 如果更适用,您也可以将通用约束移动到Thing<T>

You mention covariance, but don't actually use it. 你提到协方差,但实际上并没有使用它。 You have to specify in or out on your generic parameters. 您必须指定inout你的泛型参数。 Note that co/contravariance doesn't work on class types; 请注意,共同/逆转不适用于类类型; they must be applied to interfaces. 它们必须应用于接口。

So, introducing an interface and making it covariant: 因此,引入一个接口并使其协变:

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());
  }
}

allows this code to compile: 允许此代码编译:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM