繁体   English   中英

从泛型方法返回泛型列表

[英]Returning Generic List from Generic method

我需要使用如下代码从通用列表返回一个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));

返回时出现以下错误:

对象必须实现Iconvertible。

我确实了解templateFields并未实现IConvertible以使用ChangeType。 什么是返回templateFields的最佳方法

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

我认为这里的问题是,当您select new { ... }时,您正在选择一个匿名类型,然后Convert.ChangeType失败,因为匿名类型仅包含public read-only属性,并且未实现IConvertible 相反,我们要选择一个新的T 但是为了做到这一点,我们还必须在T上包括一个new()约束 ,这意味着T必须具有默认的构造函数(以便我们可以创建它的实例)。

这样,我们就不需要转换任何东西,因为Select的结果就是List<T>

您还可以通过在一行中选择IEnumerable<XmlNode>来减少某些代码,而不是创建第二个变量并在第一个变量上进行cast

这样的事情应该起作用:

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

您声明了接口TestData ,但未声明实现它的任何类型。 您不能将偶然碰巧具有相同属性的任何类型强制转换为该接口。 您必须创建一个类或实现它的结构。 同样,使用通常的.NET命名约定,接口名称以大写I开头,属性名称具有PascalCase。

有了这些声明...

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

你可以写

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

请注意,该方法不是通用的。 要使.ToList()创建一个IList<ITestData> ,必须将新数据(ITestData)new TestData { ... }转换为接口(ITestData)new TestData { ... }

问题是您是否仍然需要该接口,或者是否更喜欢直接使用该类。


如果仍然希望该方法是通用的,则必须告诉它T必须具有带有new()约束的默认构造函数。 并且必须使用具体类型来调用该方法。 即,您不能使用该接口调用它,因为该接口没有构造函数。

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

然后打电话给

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

暂无
暂无

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

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