从通用方法返回通用列表

时间:2019-02-02 20:59:45

标签: c# generics

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

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

我认为这里的问题是,您在进行select new { ... }时选择了一个匿名类型,然后Convert.ChangeType失败了,因为匿名类型仅包含public read-only属性,而没有实施IConvertible。相反,我们要选择一个新的T。但是,为此,我们还必须在T上包含一个new() constraint,这意味着T必须具有默认构造函数(以便我们可以创建它的实例)。 / p>

通过此操作,我们无需进行任何转换,因为List<T>导致Select

您还可以通过在一行中选择一个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();
}

答案 2 :(得分:0)

您声明的接口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 { ... }

现在的问题是,你是否还需要接口,或者如果你喜欢直接使用类。


如果您仍然希望方法是通用的,则必须告诉它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");