难以结合使用基础构造函数和非静态字段

时间:2012-07-08 00:16:00

标签: c# constructor

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

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()parms必须是静态的?

Q2 :如果在我的例子中,我们想要将非静态字段或方法与对基础构造函数的调用相结合,该怎么办?什么是OOP最多的方式呢?

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

谢谢!

更新: 我的例子太容易破解,因此我觉得我还没有学到足够的东西,所以让我们尝试给另一个(相当愚蠢的)例子:

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和base c'tor ......

4 个答案:

答案 0 :(得分:1)

  

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

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

  

Q2:如果在我的例子中,我们想要将非静态字段或方法与对基础构造函数的调用相结合,该怎么办?什么是OOP最多的方式呢?

对OOP-way来说只是简单的方法覆盖。

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

答案 1 :(得分:1)

对于 Q1 Q2 ,并不是参数必须是静态的,而是参数在调用时必须是可访问的。

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

不完全确定最终目标是什么,但它确实类似于Decorator pattern

答案 2 :(得分: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();
    }
}

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

答案 3 :(得分: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) {
    }
}