2

我被这个问题解决了太多次,所以我决定分享一下,看看你们的想法,让我们看看下面的(愚蠢的)例子:

public delegate void ToRun();
class Runner {
    ToRun tr;
    public Runner(ToRun f) {
        tr=f;
    }
    public void run() {
        tr();
    }
}
class CountingRunner : Runner {
    ToRun tr;
    int i;
    public CountingRunner(ToRun f) : base(f+=inc) {
        i=0;
    }
    private static void inc() {
        i++; //COMPILATION ERROR - i is not (and logically cannot be) static!
    }
}

好吧,我想问的是:

Q1:为什么 base() 参数必须是静态的?

Q2:如果像我的例子一样,我们想将非静态字段或方法与对基本构造函数的调用结合起来怎么办?什么是最 OOP 的方式来做到这一点?

注意:尽量不要提供像“只是不要使用基础 c'tor”这样的创可贴解决方案,因为可能会有更复杂的情况使用基础是不可避免的,所以我正在为此寻找一个合理的精心设计的解决方案。

谢谢!

更新:我的例子太容易破解,所以我觉得我学得还不够,所以让我们试着再举一个(仍然很愚蠢)的例子:

public delegate int HashFunc<E>(E e);
public interface HashTable<E> {
    void insert(E e);
    bool isMember(E e);
} 
class HashArray<E> : HashTable<E> where E : IComparable<E> {
    private E[] a;
    private bool[] taken;
    public readonly int n;
    public int size {
        get { return n; }
    }
    HashFunc<E> hash;
    public HashArray(int m , HashFunc<E> hash ) {
        n=2*m;
        a=new E[n];
        taken=new bool[n];
        for (int i=0 ; i<n ; i++) taken[i]=false;
        this.hash=hash;
    }
    public void insert(E e) {
        int index=hash(e),i;
        for (i=index ; i<n && taken[i]!=false ; ++i) ;
        if (i>=n)
            for (i=0 ; i<index && taken[i]!=false ; ++i) ;
        if (i>=index) return;
        taken[i]=true;
        a[i]=e;
    }
    public bool isMember(E e) {
        int i=hash(e);
        for ( ; i<n && taken[i]!=false && a[i].CompareTo(e)!=0 ; ++i );
        if (i>=n || taken[i]==false) return false;
        return true;
    }
}
class HashArrayInt : HashArray<int> {
    public HashArrayInt(int n) : base (n,HashFunc) {
    }
    public static int HashFunc(int i) {
        return (i%n);// n is a non static field, every hash table has its own size!
    }
}

在这个例子中,我们为哈希函数未知的哈希表提供了一些奇怪的实现,以及具有预定义哈希函数的整数哈希表的特殊类,请注意,这里我们需要再次组合哈希表 n 的非静态大小和基础c'tor...

4

4 回答 4

2

Q1:为什么 base() 参数必须是静态的?

它们必须是静态的,因为在构造函数调用时尚未定义实例(该定义“正在进行中”)。

Q2:如果像我的例子一样,我们想将非静态字段或方法与对基本构造函数的调用结合起来怎么办?什么是最 OOP 的方式来做到这一点?

OOP 方式只是简单的方法覆盖。

class Runner
{
    ToRun tr;
    public Runner(ToRun f) 
    {
        tr=f;
    }

    public virtual void Run()
    {
        tr();
    }
}

class CountingRunner : Runner {
    int i;
    public CountingRunner(ToRun f) : base(f) {
        i=0;
    }
    public override void Run() {
        i++; 
        base.Run();
    }
}
于 2012-07-08T00:27:25.037 回答
1

关于Q1Q2,并不是参数必须是静态的,而是参数必须在调用时可以访问。

并且基构造函数在本地构造函数之前被调用,这就是为什么你不能使用this成员作为参数,以及为什么你不应该调用虚拟调用。

不完全确定最终目标是什么,但它确实类似于装饰器模式

于 2012-07-08T00:29:31.227 回答
1

这就是你想要的:

class Runner {
    protected event Action _toRun;

    public Runner() {
    }
    public void Run() {
        var r = _toRun;
        if (r != null)
           _toRun();
    }


}

class CountingRunner : Runner {

    int i;
    public CountingRunner(Action f) : base() {
        _toRun += f;
    }
    public void inc() {
        i++;
    }
}

编辑

对于您使用哈希表的特定示例,此问题已通过语言设计解决。只需对哈希表的元素调用 GetHashCode() 即可确定它们的哈希码。您不需要实现来传递散列函数。

要回答您更一般的问题“我应该如何将操作实例数据的函数发送到基类”,您应该在 lambda 表达式中捕获您的实例变量并将其发送到基类,或者考虑一个设计,其中基类不需要访问其派生类的实例函数。我会选择后者:)

一种这样的设计是让函数在基类中进行纯虚拟调用。这将需要派生类来实现虚拟调用才能被实例化。所以在这里你将abstract int GetHashCode(E item)在基类中有一个函数,并在你的子类中覆盖它。同样,在这种特定情况下,语言使用GetHashCode()为所有类型定义的虚函数为您执行此操作。

这是一个非抽象示例(派生类不需要覆盖散列函数)。

class HashArray<E> : HashTable<E> where E : IComparable<E> {
    private E[] a;
    private bool[] taken;
    public readonly int n;
    public int size {
        get { return n; }
    }

    public HashArray(int m) {
        n=2*m;
        a=new E[n];
        taken=new bool[n];
        for (int i=0 ; i<n ; i++) taken[i]=false;

    }
    public void insert(E e) {
        int index= GetSpecialHashCode(e)%n;
        int i;
        for (i=index ; i<n && taken[i]!=false ; ++i) ;
        if (i>=n)
            for (i=0 ; i<index && taken[i]!=false ; ++i) ;
        if (i>=index) return;
        taken[i]=true;
        a[i]=e;
    }
    public bool isMember(E e) {
        int i= GetSpecialHashCode(e)%n;
        for ( ; i<n && taken[i]!=false && a[i].CompareTo(e)!=0 ; ++i );
        if (i>=n || taken[i]==false) return false;
        return true;
    }

    protected virtual int GetSpecialHashCode(E item) {
        return item.GetHashCode();
    }
}

所以你得到一个默认的哈希码生成函数,但也欢迎派生类提供自己的。

于 2012-07-08T00:35:08.863 回答
1

对于您的最后一个示例,我认为这可能有效:

class HashArrayInt : HashArray<int> {
    public HashArrayInt(int n) : base (n,i => HashFunc(i,n)) {
    }
    private static int HashFunc(int i, int n) {
        return (i%n);// n is a non static field, every hash table has its own size!
    }
}

如果没有,您可以这样做:

class HashFuncProvider {
    private int n;
    public HashFuncProvider(int n){
         this.n = n;
    }

    public int HashFunc(int i) {
        return (i%n);
    }
}


class HashArrayInt : HashArray<int> {
    public HashArrayInt(int n) : base (n, new HashFuncProvider(n).HashFunc) {
    }
}
于 2012-07-08T01:15:18.643 回答