Method with generic return type but not generic input. Is this possible?

五迷三道 提交于 2019-12-10 18:45:44

问题


Suppose we have a NodeData class:

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

And a base Node class and child classes that have several properties with type 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);
    }
}

As you can see there is a method which lists all outputs with type T in the Node class So I can find what are the output fields of the child class in runtime:

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

But I need to know how to use this method to list all NodeOutputs of any type T. In this example int and double. Do I need to add a method with this signature public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3. Is it possible to have method like this? return type is generic but there is no type argument for method.


回答1:


Even after your edit(s) it is not entirely clear what you are trying to achieve but here are my assumptions: -You want to have some kind of Node object that acts as a container for different types of NodeData elements. -You want to be able to return one list from this Node object that contains all NodeData elements stored in the Node container, regardless of the NodeData objects' type.

Instead of returning a List> object from the listOutputs methods, just return the non-generic version of the List object. Then you don't have to deal with T in the method call.

The logic that loops through the objects in the non-generic list can then examine the type to process the contained NodeData objects correctly.

Important note: My proposed solution is by no means pretty but I think it answers the question. In my opinion something is already seriously flawed from an OO point of view in the presented code (e.g. use of reflection) and a better solution would have to start by changing the underlying data structures. But that can only be done if we have more information how this is to be used, e.g. what kind of logic consumes the returned list.




回答2:


You can create a base interface that will be used to return the generic data.

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

I modified the function to return a list of the interface. Doing this you won't depend on 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;
    }
}

If you test the method, it should return the fields in a list. To work with a specific type, you can make use of is before using the type you search for.

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
}



回答3:


This may well be an XY problem, in that you probably want to rethink how you are designing your classes because using reflection in this way doesn't seem right. But give the problem you've presented, I'd tackle it like this:

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

And now your method signature would be:

public List<NodeDataBase> listOutputs()

And with the list returned, you can use the GetValue method to get the actual values without needing to cast to the right generic type to be able to get at the Value property.

You could also just have a return type of List<object>, but then you'll have to cast each member of that list to the right generic type before you can access it's properties.

You can also avoid that nasty reflection code, instead of having data, data1, and data2, you could simply do this in your Node class:

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

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

And now you don't even need your listOutputs method because you can just get the list from the node (unless you actually wanted a copy, but that's fairly trivial to implement).

And you TestNode would be just:

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


来源:https://stackoverflow.com/questions/31388962/method-with-generic-return-type-but-not-generic-input-is-this-possible

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!