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

发布于 2024-09-09 05:10:07 字数 996 浏览 2 评论 0 原文

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

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

由于 List 在继承树中比 IEnumerable 更低,所以不应该这样有效吗?当然,如果我新建一个 Person> ,它就会起作用,因为那是直接类型。

我正在尝试使用一种扩展方法,只要 T 实现 IEnumerable,就可以应用于所有 Person,因为我需要使用.Any() 方法。

编辑:也许我对协方差的理解不正确?我知道 IEnumerable 应该转换为 IEnumerable,但不能 IList 转换为 IEnumerable字符串>

EDIT2:忘记提及我正在使用 .net 4.0。

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? It works if I new up a Person<IEnumerable<String>> of course because that's the direct type.

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.

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>?

EDIT2: Forgot to mention that I am using .net 4.0.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

草莓味的萝莉 2024-09-16 05:10:07

我知道 IEnumerable 应该
转换为 IEnumerable,但是
无法将 IList 转换为
IEnumerable

IList 可以转换为 IEnumerable。问题是您试图将 Person> 转换为 Person>,这是非法的。例如,这样写是完全有效的:

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

因为 Value 的类型是 IEnumerable,而字符串数组是 IEnumerable。但是,您不能编写:

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

因为 Value 的类型为 List。由于您不能在所有可以使用 Person> 的地方使用 Person>,因此它不是合法的投掷。

请注意,如果您向扩展方法添加第二个类型参数,您可以执行类似于您想要的操作:

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

如果您使用 C# 4.0 并且可以使用协变,那么您可以为 person 定义一个协变接口

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.Value 被读取-仅,IPerson> 可以在 IPerson> 可以使用的任何地方使用,并且转换有效。

I know IEnumerable<String> should
convert to IEnumerable<Object>, but
couldn't IList<String> convert to
IEnumerable<String>?

IList<String> can convert to IEnumerable<String>. The problem is that you're trying to convert Person<List<String>> to Person<IEnumerable<String>>, which is illegal. 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>. However, you cannot write:

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

since Value is of type 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.

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:

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.

楠木可依 2024-09-16 05:10:07

我不确定您是否已经完全掌握了泛型的正确用法。无论如何......

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

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>确实是。

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

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.

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.

三月梨花 2024-09-16 05:10:07

您提到了协方差,但实际上并没有使用它。您必须在通用参数上指定 inout。请注意,协/逆变不适用于类类型;它们必须应用于接口。

因此,引入一个接口并使其协变:

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

You mention covariance, but don't actually use it. You have to specify in or out on your generic parameters. 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();  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文