C# 很难结合使用基本构造函数和非静态字段

C# 很难结合使用基本构造函数和非静态字段,c#,constructor,C#,Constructor,我被这个问题困扰了太多次,所以我决定与大家分享并看看你们的想法,让我们看看以下(愚蠢的)例子: 我想问的是: Q1:为什么base()参数必须是静态的 Q2:如果像我的示例中那样,我们希望将非静态字段或方法与对基本构造函数的调用结合起来,会怎么样?什么是最面向对象的方法来实现这一点 注意:尽量不要给出像“不要使用底座”这样的绷带解决方案,因为可能会有更复杂的情况,使用底座是不可避免的,所以我正在寻找一个合理、设计良好的解决方案 谢谢 更新: 我的例子太容易破解,所以我觉得我学的还不够,所以让我们

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

我想问的是:

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

Q2:如果像我的示例中那样,我们希望将非静态字段或方法与对基本构造函数的调用结合起来,会怎么样?什么是最面向对象的方法来实现这一点

注意:尽量不要给出像“不要使用底座”这样的绷带解决方案,因为可能会有更复杂的情况,使用底座是不可避免的,所以我正在寻找一个合理、设计良好的解决方案

谢谢

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

HashFunc(E)中的公共委托; 公共接口哈希表{ 空白插入(E); 布尔·伊斯梅尔(E); } 类HashArray:HashTable,其中E:IComparable{ 私人E[]a; 私人住宅; 公共只读INTN; 公共整数大小{ 获取{返回n;} } HashFunc散列; 公共哈希数组(int m,HashFunc哈希){ n=2*m; a=新的E[n]; take=新bool[n]; 对于(int i=0;i Q1:为什么base()参数必须是静态的

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

问题2:如果像在我的示例中一样,我们希望将非静态字段或方法与对基构造函数的调用相结合,那会怎么样?最适合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();
    }
}

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

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

不完全确定最终目标是什么,但它确实类似于一个。这就是你想要的:

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()即可确定它们的哈希代码。传递哈希函数不需要实现

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();
    }
}
要回答您更一般的问题“我应该如何将操作实例数据的函数发送到基类,”您应该在lambda表达式中捕获实例变量,并将其发送到基类,或者考虑基类不需要访问派生类的实例函数的设计。 一种这样的设计是让函数在基类中有一个纯虚拟调用。这需要派生类来实现虚拟调用,以便进行实例化。因此,这里有一个
abstract int GetHashCode(E项)
函数,并在子类中重写它。同样,在这种特定情况下,该语言使用为所有类型定义的虚拟
GetHashCode()
函数为您执行此操作

下面是一个非抽象示例(不需要派生类来重写哈希函数)

class HashArray:HashTable,其中E:IComparable{
私人E[]a;
私人住宅;
公共只读INTN;
公共整数大小{
获取{返回n;}
}
公共哈希数组(int m){
n=2*m;
a=新的E[n];
take=新bool[n];

对于(int i=0;i对于您的最后一个示例,我认为这是可行的:

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!
    }
}
类HashArrayInt:HashArray{
公共HashArrayInt(int n):基(n,i=>HashFunc(i,n)){
}
私有静态int HashFunc(int i,int n){
return(i%n);//n是一个非静态字段,每个哈希表都有自己的大小!
}
}
如果没有,您可以执行以下操作:

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) {
    }
}
类HashFuncProvider{
私人int n;
公共哈希函数提供程序(int n){
这个,n=n;
}
公共整数HashFunc(整数i){
返回(i%n);
}
}
类HashArrayInt:HashArray{
公共HashArrayInt(int n):基(n,新的HashFuncProvider(n).HashFunc){
}
}

你可以将构造函数设置为私有的,并使用静态的
Create
方法作为构造函数,但你将拥有更多的能力来了解如何从那里开始工作。只需将n作为散列函数的一个参数?是的,这是我想做的,但委托是由讲师指定的,因此我们无法更改它。另外,谁说有hash函数和hash大小之间没有连接?因此更改hash委托可能会强制定义n在何处是冗余的…对于初学者来说,has函数不是真正的i%n。它只是i.n是基类的私有成员变量。它不能是基类提供的hash函数的一部分,因为它不可访问e(而且不应该是)对于基类。我在我的答案中添加了一个编辑。+1表示正确的ansewr,但是从
CountingRunner
@MichaelGraczyk中删除
ToRun tr
成员,
Runner
中需要它……您将
ToRun
保存在一个成员中,以便在有人调用
Run
方法时可以调用它。@MichaelGraczyk:l在
Runner
中作为注入操作具有意义。顺便说一句,这实际上不是好的OOP,因为您需要派生类调用基类函数。设计非常糟糕。它是一种常见的反模式(一种没有帮助或有害的模式).+1用于提及装饰器模式,这可能在他的实际案例中用于OP。这不起作用,因为只有int哈希表才是可实例化的。这就是您要寻找的吗?这不可能
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) {
    }
}