views:

351

answers:

5

Given the following code:

using System.Collections.Generic;
static class Program {
 static void Main() {
  bar Bar = new bar();
  baz Baz = new baz();
  System.Console.WriteLine(
   "We have {0} bars, rejoice!", bar.Cache.Count);
 }
}

public abstract class foo {
 public static List<foo> Cache = new List<foo>(); 
}

public class bar : foo {
 public bar() { Cache.Add(this); }
}
public class baz : foo {
 public baz() { Cache.Add(this); }
}

You get the (somewhat expected) output "We have 2 bars, rejoice!".

This is fabulous, we now have twice as many places to have our beer (apparently), but what I really want is for each class to have it's own cache. The reason I don't want to just implement this cache in the subclass is because I also have some methods in my abstract class that needs to be able to operate on the cache (namely, to iterate over all of them). Is there a way to do this? I've looked at using an interface for foo, but the interface doesn't allow static members to be defined as part of the interface.

A: 

Here's your answer:

using System;
using System.Collections.Generic;
static class Program
{
    static void Main()
    {
        var bar = new Bar();
        var baz = new Baz();
        System.Console.WriteLine(
                "We have {0} bars, rejoice!", Bar.Cache.Count);

        bar.PrintList();
        baz.PrintList();
    }
}

public abstract class Foo<T>
{
    public static List<T> Cache = new List<T>();

    public void PrintList()
    {
        foreach(var item in Cache)
        {
            Console.WriteLine(item);

        }
    }
}

public class Bar : Foo<Bar>
{
    public Bar() { Cache.Add(this); }
}
public class Baz : Foo<Baz>
{
    public Baz() { Cache.Add(this); }
}
Chris Holmes
It's a cache of all the objects of that type. How does that work if it's non-static?
Matthew Scharley
"but what I really want is for each class to have it's own cache" - You either do, or you don't. Pick one.
Chris Holmes
Each class, not each instance.
siz
Each SUBCLASS to maintain it's cache, not each instance of the class, which is what taking off the static declaration would do.
Matthew Scharley
You just answered your own question. "each class". Then it's a class-level variable. Don't make this harder than it is.
Chris Holmes
Thank you monoxide :-)
Chris Holmes
I will do it that way if it's really necessary, but I'd rather avoid that situation because I have methods in my abstract class that could benefit from iterating over the cache since they only call methods defined in the abstract class, and having one generic implementation instead of copy/paste
Matthew Scharley
would be much preferable, I think we all agree
Matthew Scharley
Check my answer monoxide and see if that works for you. I actually just coded it up and tested it. I hope it's what you're looking for.
Chris Holmes
This works too, so I'll upvote to reverse the downvote someone gave you and get it back to 0, though thinkcube did beat you to the punchline :)
Matthew Scharley
A: 

try this:

using System.Collections.Generic;
using System;
static class Program
{
  static void Main()
  {
    Bar bar = new Bar();
    Baz baz = new Baz();
    System.Console.WriteLine(
            "We have {0} bars, rejoice!", bar.Cache.Count);
    System.Console.ReadKey();
  }
}

public abstract class Foo
{
  public Foo()
  {
    Cache = new List<string>();
  }
  public List<String> Cache { get; set; }
}

public class Bar : Foo
{
  public Bar() 
  { 
    Cache.Add("Bar"); 
  }
}
public class Baz : Foo
{
  public Baz() { Cache.Add("Baz"); }
}

Sorry I had to change the casing.. It was making my head explode

Shaun Bowe
+1  A: 
public abstract class foo {
    public abstract List<foo> Cache { get; }

    protected static Dictionary<Type, List<foo>> InnerCache = new Dictionary<Type, List<foo>>(); 
}

public class bar : foo {
    public override List<foo> Cache { 
       get { return foo.InnerCache[typeof(bar)]; } 
    }

    public bar() { Cache.Add(this); }
}

public class baz : foo {
    public override List<foo> Cache { 
       get { return foo.InnerCache[typeof(baz)]; } 
    }

    public baz() { Cache.Add(this); }
}
Mark Brackett
Arg, beat me to it. My exact idea.
strager
+5  A: 

Use a generic base class parameterized with the subclass:

using System.Collections;
using System.Collections.Generic;

static class Program
{
    static void Main()
    {
        bar Bar = new bar();
        baz Baz = new baz();
        System.Console.WriteLine(
                "We have {0} bars, rejoice!", Bar.GetCache().Count);
    }
}

public abstract class foo<T>
{
    private static List<foo<T> > Cache = new List<foo<T> >();

    public IList GetCache()
    {
        return Cache;
    }
}

public class bar : foo<bar>
{
    public bar() { GetCache().Add(this); }
}
public class baz : foo<baz>
{
    public baz() { GetCache().Add(this); }
}
Carlos A. Ibarra
Very nice, thankyou!
Matthew Scharley
Bah, beat me by 6 minutes. Good show.
Chris Holmes
For the record, it should probably be: public abstract class foo<T> where T : foo<T>
Matthew Scharley
@monoxide - if you want a polymorphic solution, I think Rex M's answer is much cleaner.
overslacked
@Overslacked I agree ;)
Rex M
This solution might technically have the effect you're after, but it's a bit of a kludge and relies on framework specifics more than a pure OO solution.
Rex M
It relies on the fact that foo<bar> and foo<baz> are necessarily different types. Still a clever solution no matter how you slice it.
Matthew Scharley
Since Cache is static, doesn't this mean both subclasses share the same cache? I thought one requirement was that they both maintain their own cache.
moffdub
Like I said, foo<bar> and foo<baz> ARE different classes that they are inheriting from, hence have different cache's. It's a tricky use of generics, but it works.
Matthew Scharley
+6  A: 

Each derived class of foo should define how/where to get a cache, so each can (potentially) have its own cache. Methods in foo can refer to GetCache() without the implementation being known.

public abstract class foo
{
    public abstract ICache GetCache();

    public void DoSomethingToCache()
    {
        ICache cache = this.GetCache();
        cache.DoSomething();
    }
}

public class bar : foo
{
    public static ICache BarCache = new FooCache();

    public override ICache GetCache()
    {
        return bar.BarCache;
    }
}

public class FooCache : ICache { }
Rex M
Good thing we have the "load new answers" feature. I was about to post this.
moffdub
ICache being an interface to a generic cache type? Actually, you're right, I do like this better even though thinkcube's answer was closer to what I was initially looking for. This one is more design work for me to do now though ;)
Matthew Scharley
I disagree that this is better. Now each subclass has to worry about providing its own singleton. More potential for code duplication. Instead you could still use generics to get the per class singleton and expose it once using GetCache() but each subclass is not forced to implement it.
Carlos A. Ibarra
Edited my solution to illustrate what I meant.
Carlos A. Ibarra
The reasoning behind why I think this is better is because the reason I'm caching at all is hidden behind the question:
Matthew Scharley
I'm cacheing database entries, and fetching entries from that cache may work best on id numbers for some tables, it may work better on some other field on other tables, which means different implementations. And I disagree, not EVERY class needs to make a different implementation of ICache.
Matthew Scharley
Leave singletons out of it. At the risk of sounding enterprisey, create a CacheFactory that maintains the invariant that each no more than one cache per subclass is ever handed out.
moffdub
@thinkcube - Your solution would also work, clearly, and it's an excellent example of generics. The only reason I prefer this answer is that monoxide is looking for an inherently polymorphic solution. (for the record, I +1'd both answers earlier).
overslacked
@moffdub - My approach to this would have been along those lines
overslacked
@moffdub, monoxide - to the problem, not to the question actually asked.
overslacked
@thinkcube none of us are writing production code for this problem. all of these are just very basic samples that provide launching points. this sample does not require a singleton per class any more than yours does, it just happens that I used a singleton to illustrate it.
Rex M