C
C#8mo ago
おやすみ

Any way to inherit base abstract class' constructor?

Pretty new to C#. I've got this goofy code:
public abstract class Instruction
{
protected VirtualMachine globalScope;
protected Procedure scope;
protected Traceback traceback;
public Instruction(VirtualMachine globalScope, Procedure scope, Traceback traceback)
{
ArgumentNullException.ThrowIfNull(globalScope);
ArgumentNullException.ThrowIfNull(scope);
ArgumentNullException.ThrowIfNull(traceback);
this.globalScope = globalScope;
this.scope = scope;
this.traceback = traceback;
}
public abstract void Callback();
}
public class PushZero : Instruction
{
public PushZero(VirtualMachine globalScope, Procedure scope, Traceback traceback) : base(globalScope, scope, traceback) { }
...
public abstract class Instruction
{
protected VirtualMachine globalScope;
protected Procedure scope;
protected Traceback traceback;
public Instruction(VirtualMachine globalScope, Procedure scope, Traceback traceback)
{
ArgumentNullException.ThrowIfNull(globalScope);
ArgumentNullException.ThrowIfNull(scope);
ArgumentNullException.ThrowIfNull(traceback);
this.globalScope = globalScope;
this.scope = scope;
this.traceback = traceback;
}
public abstract void Callback();
}
public class PushZero : Instruction
{
public PushZero(VirtualMachine globalScope, Procedure scope, Traceback traceback) : base(globalScope, scope, traceback) { }
...
In python, if you inherit from a class that defines a constructor, it will be inherited, and when creating an instance of that subclass, that inherited constructor is called. I know, its just a single (a bit long) line, but is there something similar in C#? (or, at least, a way to make that line shorter...)
10 Replies
Lisa
Lisa8mo ago
base keyword - C# Reference - C#
Learn about the base keyword, which is used to access members of the base class from within a derived class in C#.
おやすみ
おやすみOP8mo ago
well, if using base is the only way, then i guess i can only use that long ahh line 😭
Lisa
Lisa8mo ago
Aye, there's no implicit way to call the base constructor. You'll need to define it.
おやすみ
おやすみOP8mo ago
i just dont like the fact that i have to basically copypaste the thing into each subclass thanks for your answer
Lisa
Lisa8mo ago
Then perhaps your derived classes are redudant. If they don't need any modifications.
おやすみ
おやすみOP8mo ago
here's what i've got so far
public class PushZero : Instruction
{
public PushZero(VirtualMachine globalScope, Procedure scope, Traceback traceback) : base(globalScope, scope, traceback) { }
public override void Callback()
{
scope.instructionPointer++;
globalScope.stack.Push(0);
}
}
public class Add : Instruction
{
public Add(VirtualMachine globalScope, Procedure scope, Traceback traceback) : base(globalScope, scope, traceback) { }
public override void Callback()
{
scope.instructionPointer++;
if (!globalScope.stack.TryPeek(out _))
{
throw new CCLRuntimeError("Instruction '+' cannot add to an empty stack.", traceback);
}
}
}
public class PushZero : Instruction
{
public PushZero(VirtualMachine globalScope, Procedure scope, Traceback traceback) : base(globalScope, scope, traceback) { }
public override void Callback()
{
scope.instructionPointer++;
globalScope.stack.Push(0);
}
}
public class Add : Instruction
{
public Add(VirtualMachine globalScope, Procedure scope, Traceback traceback) : base(globalScope, scope, traceback) { }
public override void Callback()
{
scope.instructionPointer++;
if (!globalScope.stack.TryPeek(out _))
{
throw new CCLRuntimeError("Instruction '+' cannot add to an empty stack.", traceback);
}
}
}
(and some Instructions will have their own fields)
Lisa
Lisa8mo ago
I obviously don't have the entire code context here, but: My intuition says instructions could be an interface and you could pass dependencies from method arguments.
maxmahem
maxmahem8mo ago
I second using an interface, but with primary ctors, it's not that bad.
public abstract class Instruction(VirtualMachine globalScope, Procedure scope, Traceback traceback)
{
protected VirtualMachine globalScope = globalScope;
protected Procedure scope = scope;
protected Traceback traceback = traceback;

public abstract void Callback();
}

public class PushZero(VirtualMachine globalScope, Procedure scope, Traceback traceback) : Instruction(globalScope, scope, traceback) {
public override void Callback() {}
}
public abstract class Instruction(VirtualMachine globalScope, Procedure scope, Traceback traceback)
{
protected VirtualMachine globalScope = globalScope;
protected Procedure scope = scope;
protected Traceback traceback = traceback;

public abstract void Callback();
}

public class PushZero(VirtualMachine globalScope, Procedure scope, Traceback traceback) : Instruction(globalScope, scope, traceback) {
public override void Callback() {}
}
(if you really want the not-null check you can add them inline with ?? throw new ArgumentNullException())
reflectronic
reflectronic8mo ago
in general the solution for this issue is required properties they were added specifically to circumvent repeated constructor boilerplate in deep inheritance trees
public abstract class Instruction
{
public required VirtualMachine GlobalScope { get; init; }
public required Procedure Scope { get; init; }
public required Traceback Traceback { get; init; }
}

public sealed class PushZero : Instruction
{
}
public abstract class Instruction
{
public required VirtualMachine GlobalScope { get; init; }
public required Procedure Scope { get; init; }
public required Traceback Traceback { get; init; }
}

public sealed class PushZero : Instruction
{
}
then:
var p = new PushZero()
{
GlobalScope = ...
Scope = ...
Traceback = ...
};

p.Callback();
var p = new PushZero()
{
GlobalScope = ...
Scope = ...
Traceback = ...
};

p.Callback();
i am not really a fan of this design, though, in general the fact that the PushZero class doesn't define any additional state suggests, the 3 arguments should just be parameters to Callback that avoids all the boilerplate, in general
maxmahem
maxmahem8mo ago
there were originally protected, but you can do that with required properties, though some think it weird.
public abstract class Instruction {
public required VirtualMachine GlobalScope { protected get; init; }
}
public abstract class Instruction {
public required VirtualMachine GlobalScope { protected get; init; }
}
Want results from more Discord servers?
Add your server