tags:

views:

304

answers:

4

I have a generic class that has one type parameter (T). I needed to store a collection of these generic objects that are of different types, so I created an interface that the generic class implements as suggested here. There is a property in the generic class of type T that I need to access when iterating through the generic list that contains the collection of Interface objects. So far the only way I have been able to get the value is to call a method using reflection.

interface ISomeClass {

//?
}

class SomeClass<T> : ISomeClass {

 T ValueINeed { get; set;}
}

class ClassThatHasListOfGenericObjects{

 List<ISomeClass> _l = new List<ISomeClass>();

 public AddToList<T>(T someClass) : where T : ISomeClass {

 _l.Add(someClass);

 }

 public SomeMethod(){

   foreach(ISomeClass i in _l){

   i.ValueINeed; //I don't know how to access the property in the generic class

   }
 }
}
A: 

Add ValueINeed to the interface and you'll be able to call it in SomeMethod().

Sam
@Sam - I cannot add the ValueINeed to the interface, because I don't know the type and I can't make the Interface generic. Thanks for the input though.
thedugas
@thedugas, perhaps you can update the example with more realistic code? There may be another way to solve the problem.
Sam
@Sam - thanks for taking the time to look at this. I have decided to create a method in the interface that will return the correct string format according to the T ValueINeed value.
thedugas
A: 

I think you might just need a little refactoring. Looks like you're almost there

interface ISomeClass<T> {
  T ValueINeed { get; set; }
}

class SomeClass<T> : ISomeClass {

 T ValueINeed { get; set;}
}

class ClassThatHasListOfGenericObjects{

 List<ISomeClass> _l = new List<ISomeClass>();

 public AddToList<T>(T someClass) : where T : ISomeClass {

 _l.Add(someClass);

 }

 public SomeMethod(){

   foreach(ISomeClass i in _l){

   i.ValueINeed; //this will work now, since it's in the interface

   }
 }
}
Steve Danner
If you change `ISomeClass` to `ISomeClass<T>` than it cannot be used across all types like the OP requested.
Sam
A: 

The elements' types you are using is of ISomeClass, so if want to access a member property you need to either cast i to SomeClass or add the property deceleration to the interface

interface ISomeClass {
    T ValueNeeded
    {
        get;
        set;
    }
}

Note that you still need to implement the property in SomeClass.

Am
+3  A: 

As I see it you have two options. The easy option is to expose the value (as an object) on the interface (and possibly its type as well). Here's how that would look:

interface ISomeClass
{
    object ValueINeed { get; set; }
    // Only needed if you care about static type rather than using ValueINeed.GetType()
    Type TypeOfValue { get; }
}

class SomeClass<T> : ISomeClass
{
    public T ValueINeed { get; set; }
    public Type TypeOfValue { get { return typeof(T); } }

    object ISomeClass.ValueINeed { get { return ValueINeed; } set { ValueINeed = (T)value; } }
}

This has the disadvantage that there's a bit of casting going on and you might need to invoke reflection to do certain things with the value. It has the advantage that it's easy to understand and implement.

The other alternative would be to encode an "existential type" which truly represents a SomeClass<T> for some unknown T (like a SomeClass<?> in Java). This is much more complicated and hard to follow, but avoids any casts:

interface ISomeClassUser<X>
{
    X Use<T>(SomeClass<T> s);
}

interface ISomeClassUser
{
    void Use<T>(SomeClass<T> s);
}

interface ISomeClass
{
    X Apply<X>(ISomeClassUser<X> user);
    void Apply(ISomeClassUser user);
}

class SomeClass<T> : ISomeClass
{
    public T ValueINeed { get; set; }

    public X Apply<X>(ISomeClassUser<X> user) { return user.Use(this); }
    public void Apply(ISomeClassUser user) { user.Use(this); }
}

// Assumes you want to get a string out, use a different generic type as needed
class XmlUser : ISomeClassUser<string>
{
    public string Use<T>(SomeClass<T> s)
    {
        string str = "";
        // do your conditional formatting here, branching on T as needed
        // ...
        return str;
    }
}

class ClassThatHasListOfGenericObjects
{
    List<ISomeClass> _l = new List<ISomeClass>();
    XmlUser user = new XmlUser();

    public string SomeMethod()
    {
        string s = "";
        foreach (ISomeClass i in _l)
        {
            s += i.Apply(user);
        }
        return s;
    }
}
kvb
@kvb - Thank you very much for both of those suggestions. The first suggestion was inline with something I had, and it was interesting to see your implementation in the second suggestion to emulate something like Java's SomeClass<?>. I think in this particular scenario I will use something like the first suggestion, and create a method in the ISomeClass interface that converts the ValueINeed to it's formatting required for the other system in the form of a string. Thanks again for your time and knowledge.
thedugas
@kvb - BTW - I tried to +1 your answer, but since I am a StackOverflow noob I could not. Will do when I acquire some more reputation.
thedugas
@kvb - Keeping my word, +1 your answer
thedugas