2

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

   }
 }
}
3
  • Since i is a SomeClass<T> for some unknown T, what do you expect to be able to do with i.ValueINeed? Commented Feb 4, 2010 at 14:33
  • @kvb - Good point / question. In this case, the list of SomeClass<T> is being written to XML. The system the XML is being sent to is an old ERP system that has some datatypes like AlphaR, AlphaL, AlphaC, NumericN , etc... When retreiving the ISomeClass elements from the list, I would like to have the ValueINeed prop strongly typed so the methods that formats the value to the other system's type can check the .Net type and format accordingly. I know I can place a Type property in the interface and assign it in the generic class, I was just looking for a more elegant way of doing it. Commented Feb 4, 2010 at 18:31
  • cool title for the question, so +1 :) Commented Apr 18, 2013 at 10:38

4 Answers 4

6

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;
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

@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.
@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.
@kvb - Keeping my word, +1 your answer
0

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

3 Comments

@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, perhaps you can update the example with more realistic code? There may be another way to solve the problem.
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.
-1

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

   }
 }
}

1 Comment

If you change ISomeClass to ISomeClass<T> than it cannot be used across all types like the OP requested.
-1

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.

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.