具有泛型返回类型但不是通用输入的方法。这可能吗?

时间:2015-07-13 16:45:36

标签: c# generics reflection generic-programming type-parameter

假设我们有一个NodeData类:

public class NodeData<T>
{
    public string Name;
    public T Value;
    public NodeData(string name, T value)
    {
        this.Name = name;
        this.Value = value;
    }
}

基类Node类和具有多个类型为NodaData的属性的子类:

public class Node
{
    public List<NodeData<T>> listOutputs<T>()
    {
        var fieldInfos = GetType().GetFields();
        var list = new List<NodeData<T>>();
        foreach (var item in fieldInfos)
        {
            Type t = item.FieldType;
            string name = item.Name;
            if (t == typeof(NodeData<T>))
            {
                var output = new NodeData<T>(name, default(T));
                list.Add(output);
            }
        }
        return list;
    }
}

public class TestNode : Node {
    public NodeData<int> data;
    public NodeData<double> data2;
    public NodeData<double> data3;
    public TestNode ()
    {
        data = new NodeData<int>("test", 111);
        data2 = new NodeData<double>("test", 113);
    }
}

正如您所看到的,有一种方法可以在T类中列出所有类型为Node的输出。所以我可以在运行时找到子类的输出字段:

TestNode node = new TestNode ();
var list = node.listOutputs<int>(); // this returns data

但我需要知道如何使用此方法列出任何类型T的所有NodeOutput。在此示例中为intdouble。我是否需要添加具有此签名public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3的方法。有可能有这样的方法吗?返回类型是通用的,但方法没有类型参数。

3 个答案:

答案 0 :(得分:1)

即使你的编辑后,你想要实现的目标还不完全清楚,但这是我的假设: - 您希望某种Node对象充当不同类型的NodeData元素的容器。 - 您希望能够从包含Node容器中存储的所有NodeData元素的Node对象返回一个列表,而不管NodeData对象的类型如何。

而不是返回List&gt;来自listOutputs方法的对象,只返回List对象的非泛型版本。然后你不必在方法调用中处理T.

循环遍历非泛型列表中的对象的逻辑可以检查类型以正确处理包含的NodeData对象。

重要提示:我提出的解决方案绝不是很好,但我认为它回答了这个问题。在我看来,从所提出的代码中的OO观点来看,某些事情已经存在严重缺陷(例如使用反射),并且更好的解决方案必须从改变底层数据结构开始。但是,只有我们有更多信息如何使用,例如,什么样的逻辑消耗返回的列表。

答案 1 :(得分:1)

您可以创建一个用于返回通用数据的基本接口。

public interface INodeData
{
    string Name { get; }
}
public class NodeData<T> : INodeData
{
    public string Name { get; private set; }
    public T Value { get; private set; }

    public NodeData(string name, T value)
    {
        this.Name = name;
        this.Value = value;
    }
}

我修改了函数以返回接口列表。这样做,你不会依赖T

public class Node
{
    public List<INodeData> listOutputs()
    {
        var fieldInfos = GetType().GetFields();
        var list = new List<INodeData>();
        foreach (var item in fieldInfos)
        {
            INodeData data = GetType().GetField(item.Name).GetValue(this) as INodeData;
            list.Add(data);    
        }
        return list;
    }
}

如果测试方法,则应返回列表中的字段。要使用特定类型,您可以在使用搜索类型之前使用is

public class TestNode : Node
{
    public NodeData<int> data;
    public NodeData<double> data2;
    public NodeData<double> data3;

    public TestNode()
    {
        data = new NodeData<int>("test", 111);
        data2 = new NodeData<double>("test", 113);
    }
}

private static void Main(string[] args)
{
    TestNode node = new TestNode();
    var list = node.listOutputs(); // this returns data
}

答案 2 :(得分:0)

这可能是一个XY问题,因为您可能想重新考虑如何设计类,因为以这种方式使用反射似乎并不正确。但是提出你已经提出的问题,我就这样解决它:

public abstract class NodeDataBase
{
    public string Name { get; set; }

    public NodeData(string name)
    {
        this.Name = name;
    }

    // this isn't actually needed, but might be helpful
    public abstract object GetValue();

}


public class NodeData<T> : NodeDataBase
{
    public T Value { get; set; }
    public NodeData(string name, T value) : base(name)
    {
        this.Value = value;
    }

    public override object GetValue()
    {
        return Value;
    }
}

现在您的方法签名将是:

public List<NodeDataBase> listOutputs()

返回列表后,您可以使用GetValue方法获取实际值,而无需转换为正确的泛型类型,以便能够获取Value属性。

您也可以只返回List<object>的返回类型,但是您必须先将该列表的每个成员转换为正确的泛型类型,然后才能访问它的属性。< / p>

您还可以避免使用令人讨厌的反射代码,而不是datadata1data2,您可以在Node类中执行此操作:

public class Node
{
    public List<NodeDataBase> Data { get; protected set; }

    public Node()
    {
        Data = new List<NodeDataBase>();
    }
}

现在你甚至不需要你的listOutputs方法,因为你可以从节点中获取列表(除非你真的想要一个副本,但这实现起来相当简单)。

TestNode就是:

public class TestNode : Node {
    public TestNode ()
    {
        Data.Add(new NodeData<int>("test", 111));
        Data.Add(new NodeData<double>("test", 113));
    }
}