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

Let's assume we have a class NodeData

:

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

      

Both base class Node

and child classes, which 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 that lists all outputs with a type T

in the class Node

. So, I can find which fields of the output of the child class at 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

. Whether to add a method with this signature public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3

. Is it possible to have a method like this? The return type is generic, but there are no type arguments for the method.

+3


source to share


3 answers


Even after your changes 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 a single list from this Node object that contains all the NodeData elements stored in the Node container, regardless of the type of NodeData objects.

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



The logic flowing through the objects in the common generic list can then check the type to properly handle the contained NodeData objects.

Important note. My proposed solution is by no means pretty, but I think it answers the question. In my opinion, something is already seriously broken from an OO point of view in the presented code (for example, using reflection), and the best solution should start by changing the underlying data structures. But this can only be done if we have additional information on how it should be used, for example. what logic is consuming the returned list.

+1


source


You can create a base interface that will be used to return general 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 changed the function to return an interface list. In doing so, you will not 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 the list. To work with a specific type, you can use is

before using the type you are looking 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
}

      

+1


source


This could be an XY issue as you probably want to rethink the way you design your classes, because using reflection this way seems to be wrong. But give the problem you presented, I would 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 will be:

public List<NodeDataBase> listOutputs()

      

And with the returned list, you can use the method GetValue

to get the actual values ​​without having to cast to the correct generic type to be able to access the property Value

.

You can also only have a return type List<object>

, but then you will need to give each member of that list the desired generic type before you can access its properties.

You can also avoid this nasty reflection code, instead data

, data1

and data2

you could just do this in your class Node

:

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

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

      

And now you don't even need your method listOutputs

, because you can just get the list from node (unless you want a copy, but that's pretty trivial to implement).

And you TestNode

should be just:

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

      

0


source







All Articles