22

Class structure

I've attached a picture of what I'm trying to do. Let's say I have a list of T in a class

public class MyClass<T> 
    where T : IMyInterface
{
    public List<T> list = new List<T>;
}

Now, another class has a list of MyClass.

public class AnotherClass
{
    public List<MyClass<IMyInterface>> list = new List<MyClass<IMyInterface>>;
}

What is the T that I should put for MyClass? If I put T, then it assumes all types in that class are the same, but it isn't. If I put IMyInterface, I couldn't cast IMyInterface to T when accessing these classes.

new AnotherClass().list.Add(new MyClass<T>()); 

What is the type here right now? The list in AnotherClass generic type is IMyInterface but the thing I want to add in is T, which T I want to be dynamic but they are still under IMyInterface.

3
  • 5
    The problem is you're describing a solution to a problem, and not the actual problem. What are you trying to solve??? (XY Problem). Commented Apr 7, 2016 at 6:07
  • 2
    I haven't been working with c# for a while now, but I would not use generics. If you know you are putting only objects implementing your interface in it, why don't you use polymorphy. That means: public List<IMyInterface> list=new List<IMyInterface>; Commented Apr 7, 2016 at 6:13
  • 3
    you seem to be confusing things. generics are not dynamically typed. everything in C# is statically typed except statements/declarations that involve the dynamic keyword, but that has nothing to do with generics. a List<int> is a list containing ONLY ints. a List<IMyInterface> is a list containing ONLY objects implementing IMyInterface. Commented Apr 7, 2016 at 6:49

4 Answers 4

7

The T in a generic must be the same through out its lifetime.

for an example

var a = new MyClass<int>();
a.list.Add(1); //since a's generic T is declared as int the list is now also int

var b = new MyClass<string>()
b.list.Add("string"); //in b its now declared as a string and avccepts only string

So when you are doing this.

var c = new MyClass<IMyInterface>();
c.list.Add(ObjectImplementingIMyInterface); //You must add something that implements IMyInterface

The only thing you know about the content is that it implements IMyInterface now if you want to split the execution of the object then you must check the type of the object via reflection.

if(c.list[i].GetType() == typeof(ClassA_IMyInterface)
{
    //Execute on ClassA_IMyInterface
    //If you are after the ClassA 
    //and want to run something speciffic for it the cast
    ClassA clA = = (ClassA)c.list[i];
    //Now you have access to the implementation of ClassA instead of just the interface 
}
else if (c.list[i].GetType() == typeof(ClassB_IMyInterface)
{
    //Execute on ClassB_IMyInterface
}

Here is an example i made in ConsoleApplication showing how it lays out.

public class MyClass<T> where T : IMyInterface
{
    public List<T> list = new List<T>();
}
public interface IMyInterface
{
}
public class Foo : IMyInterface
{
}
public class Bar : IMyInterface
{
}
public class FooBar
{
    public void Test()
    {
        var content = new MyClass<IMyInterface>();
        content.list.Add(new Foo());
        if (content.list[0] is Foo)
        {
            //Execute on Foo 
            var g = (Foo)content.list[0];
            //Now you can access Foo's methods and not only the Interfaces
            Console.WriteLine("Foo");
        }
        else if (content.list[0] is Bar)
        {
            //Execute on Bar
            var g = (Bar)content.list[0];
            //Now you can access Bar's methods and not only the Interfaces
            Console.WriteLine("Bar");
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

c.list[0] is dd1. This allows subclasses of dd1 to be treated with the code for dd1.
@Taemyr is is a bool evaluator (and i could probably use it instead of GetType/typeof. Are you thinking of as (close to casting, but allows for the result to be null)
I mean instead of GetType(). In your example using as would not make sense.
yes using is like that, would make it a little nicer as it is a syntactic sugar.
6

I think your issue is that you do not need to use generics for this. If you specify your class as:

public class MyClass
{
    public IList<IMyInterface> list = new List<IMyImterface>();
}

Then you can add to this any instance of any class implementing the interface.

public class DummyClass : IMyInterface {}

public class AnotherClass {
{
    IList<MyClass> anotherList = new List<MyClass>();

    public void AMethod()
    {
        MyClass myList = new MyClass();
        myList.Add(new DummyClass());

        anotherList.Add(myList);
    }
}

Then you can access all items from the list as IMyInterface directly, or if you want items of a specific class, you can use LINQ:

foreach (IMyImterface item in myList.Where(x => x is DummyClass))
{
    DummyClass dummy = (DummyClass)item;
}

// or
foreach (IMyImterface item in myList.OfType<DummyClass>())
{
    DummyClass dummy = (DummyClass)item;
}

Or you can just try casting and check if null:

foreach (IMyIntetface item in myList)
{
    DummyClass dummy = item as DummyClass;
    if (dummy != null)
    {
        // do something
    }
}

4 Comments

the canonical way of getting all instances of a specific subtype from a sequence is seq.OfType<T>() (your version does two type checks). although you shouldn't be downcasting stuff in 99 out of 100 cases anyway.
Thanks for the tip. I expect that would be faster?
well I would assume the time-complexity is the same, but I wouldn't really worry about the performance, both are fast enough to never ever be the bottle-neck in your app. it's just cleaner to say what you want, rather than saying what something should do, knowing it will result in what you want. it's kind of a declarative vs imperative thing.
@SteveHarris I think the cast (DummyClass)item will throw an exception rather than return null. It should be item as DummyClass.
5

I think you mean that you want to have multiple generic types in one list eg:

var ac = new AnotherClass();
ac.list.Add(new MyClass<int>()); 
ac.list.Add(new MyClass<bool>()); 
ac.list.Add(new MyClass<double>());

A simple way to do this is to define your list as a list of object:

public class AnotherClass
{
    public List<object> list = new List<object>();
}

The issue here is you could anything you like to that list, not just your generic class. The following would also be possible:

list.Add(1); 
list.Add("hello"); 

The only way you could restrict what you can add is to use an abstract base class.

public class MyClassBase 
{

}
public class MyClass<T> : MyClassBase
    where T : IMyInterface
{
    public List<T> list = new List<T>();
}

And then have a list of those:

public class AnotherClass
{
    public List<MyClassBase> list = new List<MyClassBase>();
}

You'll still have to do some type checking when accessing those objects but at least you'll know that the objects are restricted to those inheriting MyClassBase

Comments

3

Using covariant?

public interface IMyInterface { }
public class A : IMyInterface { }
public class B : IMyInterface { }

// Covariant interface
public interface IMyClass<out T> { }

// Inherit IMyClass
public class MyClass<T> : IMyClass<T> where T : IMyInterface 
{ 
    public List<T> list = new List<T>(); 
}

public class AnotherClass
{
    // Note the list using IMyClass instead of the concrete MyClass type
    // The covariant interface type would allow any IMyInterface conversion
    public List<IMyClass<IMyInterface>> list = new List<IMyClass<IMyInterface>>();

    public AnotherClass()
    {
        this.list.Add(new MyClass<A>());
        this.list.Add(new MyClass<B>());
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.