Add and get object in list by type

95 Views Asked by At

Suppose I have a bunch of objects A, B, C, ... that implement multiple interfaces I1, I2, I3, ...

My objective is to have functions like these

public void AddObjectThatImplements<T>(T o){
    //add o to some data structure that stores objects that implement T
}

public List<T> GetInterfaces<T>(){
    //return all T instances from the list of who implements T
}

In the initialization method of each of these objects, I can add their interfaces into a any kind of data structure that can help making these functions work.

For simplicity, all interfaces I1, I2, ... inherit from a base interface I0.

How can I build such functions? Any additional data structure is welcome.

4

There are 4 best solutions below

2
wohlstad On

You can store all your objects in a List<I0>.
Then in GetInterfaces use the is operator, to build a new list of all objects in the first one that are is T.

From the documentation, the is operator is true in particular when:

  • The run-time type of an expression result is T.
  • The run-time type of an expression result derives from type T, implements interface T, or another implicit reference conversion exists from it to T.

I am aware that it ignores T in AddObjectThatImplements but it seems to implement the behavior you need.

Therefore in the example below I renamed AddObjectThatImplements to simply AddObject:

public class Example
{
    private List<I0> all_objects = new List<I0>();

    public void AddObject(I0 o)
    {
        all_objects.Add(o);
    }

    public List<T> GetInterfaces<T>()
    {
        List<T> list = new List<T>();
        foreach(I0 o in all_objects)
        {
            if (o is T)
            {
                list.Add((T)o);
            }
        }
        return list;
    }
}

Update:
As commented below, GetInterfaces can be implemented more elegantly using LINQ (requires using System.Linq;) - keeping the same behavior:

public List<T> GetInterfaces<T>()
{
    return all_objects.OfType<T>().ToList();
}
3
Daniel On

Maybe this is the way to go:

public static Dictionary<Type, List<I0>> data = new();

public static void AddObjectThatImplements<T>(T t) where T : I0
{
    if (!data.ContainsKey(typeof(T))) data[typeof(T)] = new();
    data[typeof(T)].Add(t);
}

public static List<T> GetInterfaces<T>() where T : I0
{
    if (!data.ContainsKey(typeof(T))) return new();
    return data[typeof(T)].OfType<T>().ToList();
}

I'm still trying to find out how to remove this OfType from the GetInterfaces though... Looks like it can't cast from I0 to T directly (which is normal since T is a subclass of I0) so I need this currently.

0
Unseen On

There is sample instance test for interfaces and classes.

internal class Program
{
    private static void Main(string[] args)
    {
        RootObject instance = new();
        bool result = instance is BaseObject;
        Console.WriteLine(result);

        bool resultInterfaceCheck = instance is IBaseObject;
        Console.WriteLine(resultInterfaceCheck);


        var resultBothCheck = CheckInstanceType<BaseObject, IBaseObject>(instance);

        Console.WriteLine(resultBothCheck);

        // SAMPLE OF YOUR QUESTION

        List<RootObject> rootObjects = [];

        if (resultBothCheck)
        {
            rootObjects.Add(instance);
        }

        Console.ReadLine();

    }

    public static bool CheckInstanceType<T, K>(object instance)
        where T : class
        where K : IBaseObject
    {

        Type instanceType = instance.GetType();

        return instanceType.IsSubclassOf(typeof(T)) || typeof(K).IsAssignableFrom(instanceType);
    }
}

public class RootObject : BaseObject, IBaseObject
{ }

public class BaseObject : IBaseObject
{ }

public interface IBaseObject { }



But you can also use it in static extension method like this:

public static class Extensions
{
    public static bool CheckInstanceType<T, K>(this object instance)
        where T : class
        where K : IBaseObject
    {

        Type instanceType = instance.GetType();

        return instanceType.IsSubclassOf(typeof(T)) || typeof(K).IsAssignableFrom(instanceType);
    }
}


public class UsedIn
{
    List<RootObject> rootObjects = [];
    RootObject instance = new();

    public void Add()
    {
        if (instance.CheckInstanceType<BaseObject, IBaseObject>())
        {
            rootObjects.Add(instance);
        }
    }

}




Either way if you want to use it for multiple type of interfaces you can just mark all that interfaces with BaseInterface

public interface IBaseObject : IMarkedInterface { }

public interface IShapedObject : IMarkedInterface { }

public interface IMarkedInterface { }

public static class Extensions
{
    public static bool CheckInstanceType<T, K>(this object instance)
        where T : class
        where K : IMarkedInterface
    {

        Type instanceType = instance.GetType();

        return instanceType.IsSubclassOf(typeof(T)) || typeof(K).IsAssignableFrom(instanceType);
    }
}

0
Enigmativity On

Give your requirements it is super simple just to use the built-in List<T> and OfType<T>() code.

Just like this:

var instances = new List<I0>();

var instances_I2 = instances.OfType<I2>();

There doesn't seem to be a compelling reason to do more than this, unless you're not explaining your full problem.