抽象基类还是接口?似乎都不太合适。

4

给定以下代码:

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); }
}

你会得到(有点意料之中的)输出:“我们有2个酒吧,欢呼吧!”。这很棒,现在我们有了两倍的地方可以喝啤酒(显然),但我真正想要的是每个类都有自己的缓存。我不想只是在子类中实现这个缓存,因为我还有一些在抽象类中需要能够操作缓存的方法(即迭代所有缓存)。有没有办法做到这一点?我已经尝试使用接口来定义foo,但接口不允许将静态成员定义为接口的一部分。
5个回答

8

foo的每个派生类都应该定义如何/在哪里获取缓存,因此每个派生类都可以(潜在地)有自己的缓存。在foo中的方法可以引用GetCache(),而不需要知道其实现方式。

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 { }

ICache是通用缓存类型的接口吗?实际上,你是对的,尽管thinkcube的答案更接近我最初寻找的内容。不过现在这个需要我做更多的设计工作 ;) - Matthew Scharley
1
我不同意这是更好的。现在每个子类都必须担心提供自己的单例。这样会增加代码重复的可能性。相反,您仍然可以使用泛型来获取每个类的单例,并使用GetCache()一次公开它,但不需要强制每个子类实现它。 - Carlos A. Ibarra
修改了我的解决方案,以说明我的意思。 - Carlos A. Ibarra
我正在缓存数据库条目,并且从该缓存中获取条目可能最适合某些表的id号,对于其他表的某些其他字段可能更好,这意味着不同的实现。而且我不同意,不是每个类都需要对ICache进行不同的实现。 - Matthew Scharley
@moffdub, monoxide - 我们应该解决问题,而不是仅回答所提出的问题。 - overslacked
显示剩余6条评论

4

使用一个以子类为参数化类型的通用基类:

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); }
}

记录一下,它应该是这样的:public abstract class foo<T> where T : foo<T> - Matthew Scharley
@monoxide - 如果你想要一个多态的解决方案,我认为Rex M的答案更加简洁。 - overslacked
这个解决方案在技术上可能会产生你想要的效果,但它有点笨拙,并且更多地依赖于框架细节而不是纯面向对象的解决方案。 - Rex M
它依赖于 foo<bar> 和 foo<baz> 必然是不同类型的事实。无论如何,这仍然是一个聪明的解决方案。 - Matthew Scharley
由于缓存是静态的,这是否意味着两个子类共享同一个缓存?我认为其中一个要求是它们都要维护自己的缓存。 - moffdub
就像我之前所说的,foo<bar>和foo<baz>是从不同类继承而来的,因此它们有不同的缓存。这是泛型的一个棘手用法,但它确实有效。 - Matthew Scharley

1
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); }
}

太快了,抢先一步了。这正是我的想法。 - strager

0

这是你的答案:

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); }
}

这是该类型所有对象的缓存。如果它不是静态的,那么它是如何工作的? - Matthew Scharley
每个子类都要维护它自己的缓存,而不是类的每个实例,这就是取消静态声明所做的事情。 - Matthew Scharley
你刚刚回答了自己的问题。“每个类”。那么它就是一个类级别的变量。不要把它弄得比实际情况更难。 - Chris Holmes
如果真的必要,我会那样做,但我宁愿避免这种情况,因为我的抽象类中有一些方法可以从迭代缓存中受益,因为它们只调用在抽象类中定义的方法,并且只有一个通用实现而不是复制/粘贴。 - Matthew Scharley
这也可以,所以我会点赞来取消某人给你的踩,并将其恢复为0,尽管thinkcube已经先你一步了 :) - Matthew Scharley
显示剩余4条评论

-1

试试这个:

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"); }
}

抱歉我不得不更改大小写.. 它让我的头要爆炸了


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接