Generics Question

Hi there,

Suppose I have the following class:

public class CacheItem<T>
{
public T Data
{
get { ... }
}

public bool IsExpired
{
get { ... }
}

//other properties
}

How would I go about iterating over a collection of cache items where T could be different for each cache item In other words, how do I get this to work:

foreach (CacheItem< > cacheItem in Cache.Items)
{
if (cacheItem.IsExpired) { ... }
}

As far as I can tell, this is impossible. The best I could do would be to define a non-generic ICacheItem interface and code against that. Then I could provide the generic CacheItem<T> class as a useful implementation of ICacheItem.

Does anyone have further insight into this problem

Thanks,
Kent



Answer this question

Generics Question

  • Gage

    Thanks, but that does not help. All the items in the stack are of the same type (ie. they have the same T). In my case, I'm trying to iterate over CacheItem< >, not CacheItem<T>.

    Kent


  • Diganta Roy

    Uh, no offense, but did you even read the thread What you mentioned here was suggested similar several times, even by the original poster in the very first post:

    "The best I could do would be to define a non-generic ICacheItem interface and code against that. Then I could provide the generic CacheItem<T> class as a useful implementation of ICacheItem."


  • MarkBosley

    Generics could be your solution here, but the point is that your class is not generic. Try this (the generic T replaces your non-generic class):

    public interface IExpirable
    {
    bool IsExpired { get
    ; }
    }

    public class Cache<T> where T : IExpirable
    {
    public void
    ClearCache()
    {
    foreach (T cacheItem in
    Items)
    {
    if
    (cacheItem.IsExpired) { }
    }
    }

    public
    T[] Items
    {
    get { return null
    ; }
    }
    }

    HTH!!!



  • aaa

    Well, just because I was curious, I tried a few things, but all, maybe your attempts, too, to cast a strong type to a "partially known" type defeat the purpose of generics to define strong types and avoid casting. I just tried to figure out if the elements of the list are of the same abstract generic type and call the known method on them if so:

    class Program
    {
    static void Main(string[] args)
    {
    ArrayList list = new ArrayList();
    list.Add(
    new TG1());
    list.Add(
    new TG2());
    foreach (object obj in list)
    {
    Type type = obj.GetType();
    Type genericType = obj.GetType().BaseType.GetGenericTypeDefinition();
    if (genericType == typeof(TG<>))
    {
    PropertyInfo isExpiredProperty = type.GetProperty("IsExpired");
    Console.WriteLine(isExpiredProperty.GetValue(obj, null));
    }
    }
    }
    }

    public class TG<T> where T : class
    {
    private bool isExpired = false;
    public bool IsExpired
    {
    get { return this.isExpired; }
    set { this.isExpired = value; }
    }
    }

    public class T1 {}

    public class T2 {}

    public class TG1 : TG<T1> {}

    public class TG2 : TG<T2>{}

    This works, but using an interface will be significantly faster during runtime, because you do not have to go through reflection.
    I think what you would need is called a variance cast, so e.g. casting TG<T1> to TG<object> as a supertype which does not really exist, but afaik none of the popular programming languages supports that yet.


  • CoolMicheal

    Hi,

    I unmarked the above as the answer because I can't see how it helps me implement the following:

    foreach (CacheItem< > cacheItem in Cache.Items)
    {
    if (cacheItem.IsExpired) { ... }
    }

    If someone could point out how I would implement that loop I'd be extremely grateful.

    Thanks,
    Kent


  • Etumnamboa

    Thanks. That is the approach I mentioned in my original post, and is the only way I could get it to work.

    What I was wondering was whether there was way to tell the compiler that I didn't know the exact type for T, but I wasn't going to access any members of the cache items that "relied on" knowing T. I'm not sure on the definition of "relied on" here.

    I guess I was just hoping to avoid the extra interface because it clutters my API. Also, this would be a problem for existing types that haven't defined a corresponding non-generic interface. For example:

    List<Nullable< >> nullables = ...;

    foreach (Nullable< > nullable in nullables)
    {
    if (nullable.HasValue) { ... }
    }

    What we're saying is that this just won't work, and there is no way around it but to provide the exact type of nullables in use.

    Thanks for all the input - keep it coming if you have more.

    Kent


  • Marcin Kosieradzki

    Hi,
    check this article: http://msdn2.microsoft.com/en-US/library/ee5kxzk0.aspx
    See if it helps.


  • SteveTr

    Sorry, I misunderstood. I don't know how to solve it but I became interested in this question so I did a search in google and found this: http://blogs.msdn.com/brada/archive/2004/01/27/63739.aspx
    Not exactly the solution but the answer may be through reflection.

    I'll look into it tomorrow at night, after work, if, in the meanwhile, no one posts the answer.


  • Thomas Delrue

    [Edit]: After posting I realized this is exactly what John.Doe is refering too...

     

    If each item in your collection that you're enumerating from all derive from a specific base class you can use that base class in your foreach statement.

    For your example lets suppose we have an interface IExpirable such that:

    public interface IExpirable
    {
    bool IsExpired { get;}
    }

    And all of your cache items in your cache implement this interface you can use the following foreach loop:

    foreach(CacheItem<IExpirable> cacheItem in Cache.Items)
    {
       
    if(cacheItem.IsExpired)...
    }

     

    I hope this helps.



  • Peter H

    Well, as the cache items are not of the same type CacheItem<A> is a different type than CachItem<B> there is no common underlying type except of Object. So the iteration using CacheItem< > is not possible, I would go ahead and implement an interface that specifies IsExpired and use the interface in the foreach loop.
  • Generics Question