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

本文关键字:泛型 方法 输入 返回类型 | 更新日期: 2023-09-27 18:02:31

假设我们有一个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);
    }
}

正如你所看到的,有一个方法列出了Node类中T类型的所有输出,所以我可以找到运行时子类的输出字段:

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。有可能有这样的方法吗?返回类型为泛型,但方法没有类型参数。

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

即使在你的编辑之后,你也不完全清楚你想要达到什么目的,但这里是我的假设:-需要某种Node对象作为不同类型NodeData元素的容器。-您希望能够从这个Node对象返回一个列表,该列表包含存储在Node容器中的所有NodeData元素,而不管NodeData对象的类型。

不是从listOutputs方法返回List>对象,而是返回List对象的非泛型版本。这样就不必在方法调用中处理T了。

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

重要提示:我提出的解决方案一点也不漂亮,但我认为它回答了问题。在我看来,从面向对象的角度来看,所呈现的代码中已经存在一些严重的缺陷(例如反射的使用),更好的解决方案必须从改变底层数据结构开始。但是,这只有在我们有更多关于如何使用它的信息的情况下才能做到,例如,什么样的逻辑使用返回的列表。

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

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
}

这很可能是一个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>,但在你可以访问它的属性之前,你必须将该列表的每个成员强制转换为正确的泛型类型。

您还可以避免那些讨厌的反射代码,而不是data, data1data2,您可以简单地在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));
    }
}