简体   繁体   English

从泛型方法返回泛型列表

[英]Returning Generic List from Generic method

I need to return a genericList templateFields as below from a generic list with code as below: 我需要使用如下代码从通用列表返回一个genericList templateFields ,如下所示:

public interface TestData
{
    string field { get; set; }
    string fieldName { get; set; }
    string type { get; set; }
}

private static IList<T> GETCG<T>(string test, string type) where T : Program.TestData
{
    XmlNodeList extractNode = xdoc.SelectNodes(
       @".//mediaInstances/mediaInstance/properties/templateFields/templateField", manager);
    var nodees = new List<XmlNode>(extractNode.Cast<XmlNode>());
    var templateFields = nodees.Cast<XmlNode>().Select(x => new
    {
        field = (String)x.Attributes["userName"].Value,
        fieldName = (String)x.Attributes["name"].Value
            .Substring(0, x.Attributes["name"].Value.IndexOf(':')),
        type = (String)x.Attributes["name"].Value.Substring(x.Attributes["name"].Value
            .IndexOf(':') + 1, 4)                          
    }).ToList();
}

return (T)Convert.ChangeType(templateFields, typeof(T));

I get the following error, on the return: 返回时出现以下错误:

Object must implement Iconvertible. 对象必须实现Iconvertible。

I do understand templateFields doesnot implement IConvertible to use ChangeType. 我确实了解templateFields并未实现IConvertible以使用ChangeType。 What's the best way of returning templateFields 什么是返回templateFields的最佳方法

Add new() contraint to T and use the following codee new()约束添加到T并使用以下代码

private static IList<T> GETCG<T>(string test, string type) where T : TestData, new()
{
    XmlNodeList extractNode = xdoc.SelectNodes(@".//mediaInstances/mediaInstance/properties/templateFields/templateField", manager);
    var nodees = new List<XmlNode>(extractNode.Cast<XmlNode>());
    var templateFields = nodees.Cast<XmlNode>().Select(x => new T() //not anonymous type but T object
    {
        field = x.Attributes["userName"].Value,
        fieldName = (string)x.Attributes["name"].Value.Substring(0, x.Attributes["name"].Value.IndexOf(':')),
        type = x.Attributes["name"].Value.Substring(x.Attributes["name"].Value.IndexOf(':') + 1, 4)

    }).ToList();

    return templateFields;
}

I think the problem here is that you're selecting an anonymous type when you do select new { ... } , and then the Convert.ChangeType fails because anonymous types only include public read-only properties, and don't implement IConvertible . 我认为这里的问题是,当您select new { ... }时,您正在选择一个匿名类型,然后Convert.ChangeType失败,因为匿名类型仅包含public read-only属性,并且未实现IConvertible Instead, we want to select a new T . 相反,我们要选择一个新的T But in order to do this, we also have to include a new() constraint on T , which means that T must have a default constructor (so we can create an instance of it). 但是为了做到这一点,我们还必须在T上包括一个new()约束 ,这意味着T必须具有默认的构造函数(以便我们可以创建它的实例)。

By doing this, we don't need to convert anything, as we have a List<T> as a result of the Select . 这样,我们就不需要转换任何东西,因为Select的结果就是List<T>

You can also reduce some code by selecting an IEnumerable<XmlNode> in one line, rather than creating a second variable and doing a cast on the first one. 您还可以通过在一行中选择IEnumerable<XmlNode>来减少某些代码,而不是创建第二个变量并在第一个变量上进行cast

Something like this should work: 这样的事情应该起作用:

private static IList<T> GETCG<T>(string test, string type) where T : TestData, new()
{
    IEnumerable<XmlNode> templateFieldNodes = xdoc
        .SelectNodes(".//mediaInstances/mediaInstance/properties/templateFields/templateField", 
            manager)
        .Cast<XmlNode>();

    return templateFieldNodes.Select(x => new T
    {
        field = (String)x.Attributes["userName"].Value,
        fieldName = (String)x.Attributes["name"].Value
            .Substring(0, x.Attributes["name"].Value.IndexOf(':')),
        type = (String)x.Attributes["name"].Value.Substring(x.Attributes["name"].Value
            .IndexOf(':') + 1, 4)
    }).ToList();
}

You declared an interface TestData but didn't declare any type implementing it. 您声明了接口TestData ,但未声明实现它的任何类型。 You cannot cast any type that just happens to have the same properties by accident to this interface. 您不能将偶然碰巧具有相同属性的任何类型强制转换为该接口。 You must create a class or struct implementing it. 您必须创建一个类或实现它的结构。 Also, with the usual .NET naming conventions interface names start with an upper case I and property names have PascalCase. 同样,使用通常的.NET命名约定,接口名称以大写I开头,属性名称具有PascalCase。

With these declarations ... 有了这些声明...

public interface ITestData
{
    string Field { get; set; }
    string FieldName { get; set; }
    string Type { get; set; }
}

public class TestData : ITestData
{
    public string Field { get; set; }
    public string FieldName { get; set; }
    public string Type { get; set; }
}

You can write 你可以写

private static IList<ITestData> GETCG(string test, string type)
{
    XmlNodeList extractNode = xdoc.SelectNodes(
        @".//mediaInstances/mediaInstance/properties/templateFields/templateField", manager);
    var nodees = new List<XmlNode>(extractNode.Cast<XmlNode>());
    var templateFields = nodees.Cast<XmlNode>().Select(x => (ITestData)new TestData {
        Field = (String)x.Attributes["userName"].Value,
        FieldName = (String)x.Attributes["name"].Value
            .Substring(0, x.Attributes["name"].Value.IndexOf(':')),
        Type = (String)x.Attributes["name"].Value.Substring(x.Attributes["name"].Value
            .IndexOf(':') + 1, 4)
    }).ToList();
    return templateFields;
}

Note that the method is not generic. 请注意,该方法不是通用的。 To make .ToList() create a IList<ITestData> , the new data must be casted to the interface (ITestData)new TestData { ... } . 要使.ToList()创建一个IList<ITestData> ,必须将新数据(ITestData)new TestData { ... }转换为接口(ITestData)new TestData { ... }

The question is whether you still need the interface, or if you prefer to use the class directly. 问题是您是否仍然需要该接口,或者是否更喜欢直接使用该类。


If you still want the method to be generic, you must tell it that T must have a default constructor with the new() constraint. 如果仍然希望该方法是通用的,则必须告诉它T必须具有带有new()约束的默认构造函数。 And you must call the method with a concrete type. 并且必须使用具体类型来调用该方法。 Ie, you cannot call it with the interface, since this one does not have a constructor. 即,您不能使用该接口调用它,因为该接口没有构造函数。

private static IList<T> GETCG<T>(string test, string type)
    where T : ITestData, new()
{
    ...
    var templateFields = nodees.Cast<XmlNode>().Select(x => new T {
       ...
    }).ToList();
    return templateFields;
}

and call with 然后打电话给

IList<TestData> var result = GETCG<TestData>("hello", "world");

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

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