从基 class C# 获取隐藏成员
Getting hidden member from base class C#
在 TIBCO Spotfire 中(我无法控制他们的代码),有一个抽象的 class - DocumentNode 有一个 属性 像这样实现的事务(根据反汇编):
public ITransactions Transactions
{
[ApiVersion("2.0")] get => (ITransactions) this;
}
那么DocumentNode实现
public interface ITransactions
{
AggregatedTransactionHandle BeginAggregatedTransaction();
void ExecuteInvisibleTransaction(Executor executor);
void ExecuteStickyTransaction(Guid guid, Executor executor);
void ExecuteTransaction( Executor executor);
}
执行者在哪里
delegate void Executor()
DocumentNode(同样根据反汇编)实现接口如下:
[ApiVersion("2.0")]
void ITransactions.ExecuteTransaction(Executor executor) => this.Transaction("Anonymous transaction", executor);
[ApiVersion("2.0")]
void ITransactions.ExecuteInvisibleTransaction(Executor executor) => this.InvisibleTransaction(executor);
[ApiVersion("2.0")]
void ITransactions.ExecuteStickyTransaction(Guid guid, Executor executor) => this.StickyTransaction(guid, executor);
[ApiVersion("2.0")]
public INodeContext Context
{
[ApiVersion("2.0")] get => (INodeContext) this;
}
[ApiVersion("2.0")]
public ITransactions Transactions
{
[ApiVersion("2.0")] get => (ITransactions) this;
}
但仅通过事务公开 属性 而不是直接通过 DocumentNode。
我有一定控制权的代码有许多 class 继承自 DocumentNode 并过度使用 ExecuteTransaction 导致嵌套事务。
典型用途:
this.Transactions.ExecuteTransaction(...
Spotfire 内部有一些规则,规定当内部事务启动时外部事务可以处于什么状态,并且当状态不匹配时它会抛出异常。我无法访问该内部状态,但我会对这样的解决方案感到满意,如果捕获到此类异常,我们 运行 执行程序而不启动事务。
我还需要将代码更改保持在最低限度(我就是这么做的)。
所以我打算把一些从 DocumentNode 继承的 classes 改为从 DocumentNodeExt
继承
public class DocumentNodeExt : DocumentNode
{
public DocumentNodeExt() : base()
{ }
public DocumentNodeExt(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
public new ITransactions Transactions
{
get { return this; }
}
public AggregatedTransactionHandle BeginAggregatedTransaction()
{
return base.Transactions.BeginAggregatedTransaction(); // stack overflow
// return ((DocumentNode)this).Transactions.BeginAggregatedTransaction(); // stack overflow
}
public void ExecuteInvisibleTransaction(Executor executor)
{
try
{
base.Transactions.ExecuteInvisibleTransaction(executor);
}
catch (InvalidOperationException)
{
executor();
}
}
public void ExecuteStickyTransaction(Guid guid, Executor executor)
{
base.Transactions.ExecuteStickyTransaction( guid, executor);
}
void ExecuteTransaction(Executor executor)
{
try
{
base.Transactions.ExecuteTransaction(executor);
}
catch (InvalidOperationException)
{
executor();
}
}
}
问题来了:因为 DocumentNode.Transactions 不是虚拟的,所以我无法在派生的 class 中覆盖它。我只能通过“新”隐藏它,
但是我该如何调用 DocumentNode 的 ExecuteTransaction 实现呢?
base 没有方法 ExecuteTransaction 并且 base.Transactions 与 this.Transactions.
相同
我可以忘记隐藏事务并简单地实现 ITransactions,但是我还需要更改来自
的典型调用
this.Transactions.ExecuteTransaction(...
到
this.ExecuteTransaction(...
这比我的“配额”允许的代码更改更多。
您只能包装 ITransactions
成员。
public class DocumentNode : ITransactions
{
void ITransactions.ExecuteInvisibleTransaction() => Console.WriteLine("ExecuteInvisibleTransaction in DocumentNode");
public ITransactions Transactions => this;
}
public class DocumentNodeExt : DocumentNode
{
public new ITransactions Transactions { get; }
public DocumentNodeExt()
{
Transactions = new TransactionsWrapper(this);
}
}
public class TransactionsWrapper : ITransactions
{
private readonly ITransactions _transactions;
public TransactionsWrapper(ITransactions transactions)
{
_transactions = transactions;
}
public void ExecuteInvisibleTransaction()
{
Console.WriteLine("ExecuteInvisibleTransaction in TransactionsWrapper");
_transactions.ExecuteInvisibleTransaction();
}
}
public interface ITransactions
{
void ExecuteInvisibleTransaction();
}
https://dotnetfiddle.net/fzU3e1
注意 DocumentNodeExt
不继承接口,只有 ITransaction
成员的包装器继承。
继承与基础相同的接口class并调用基础成员是导致堆栈溢出的原因。
由于我无法理解为什么会发生此堆栈溢出,因此我为此打开了一个单独的问题
I could forget hiding Transactions and simply implement the ITransactions, but then I would also need to change the typical calls from
this.Transactions.ExecuteTransaction(...
to
this.ExecuteTransaction(...
我不明白为什么。让我们确定一些事情:
this
是从 DocumentNode
. 继承的某种类型
DocumentNode
实施 ITransaction
.
Transactions
是非虚拟的。
Transactions
在基数 DocumentNode
中定义为 (ITransaction) this
.
从 1.、2. 和 4. 可以看出,对于 DocumentNode
对 Transactions
的实施,以下始终为真:
this.ReferenceEquals(this.Transactions)
因为 4. 它必须在所有不隐藏 Transactions
的子类中为真,因为它们无法覆盖它。所以只要你没有在子类中做任何奇怪的隐藏,对 this.ExecuteTransaction
和 this.Transactions.ExecuteTransaction
的调用就必须相同。
要将其放入代码中,请考虑您的代码的这个精简版本:
public interface ITransactions
{
void ExecuteTransaction();
}
public class DocumentNode : ITransactions
{
void ITransactions.ExecuteTransaction() => Console.WriteLine("Base ExecuteTransaction");
public ITransactions Transactions => (ITransactions) this;
}
public class ExtDocumentNode : DocumentNode, ITransactions
{
public void ExecuteTransaction() => Console.WriteLine("Overriden ExecuteTransaction");
}
public class Implementation : DocumentNode
{
public void Foo() => this.Transactions.ExecuteTransaction();
}
public class ExtImplementation : ExtDocumentNode
{
public void Foo() => this.Transactions.ExecuteTransaction();
}
现在代码如下:
var instance = new Implementation();
var extInstance = new ExtImplementation();
instance.Foo();
extInstance.Foo();
打印
Base ExecuteTransaction
Overriden ExecuteTransaction
符合预期(参见 demo)。
因此,如前所述,我要么完全没有抓住要点,要么您的问题不是问题。如有不妥请指正
在 TIBCO Spotfire 中(我无法控制他们的代码),有一个抽象的 class - DocumentNode 有一个 属性 像这样实现的事务(根据反汇编):
public ITransactions Transactions
{
[ApiVersion("2.0")] get => (ITransactions) this;
}
那么DocumentNode实现
public interface ITransactions
{
AggregatedTransactionHandle BeginAggregatedTransaction();
void ExecuteInvisibleTransaction(Executor executor);
void ExecuteStickyTransaction(Guid guid, Executor executor);
void ExecuteTransaction( Executor executor);
}
执行者在哪里
delegate void Executor()
DocumentNode(同样根据反汇编)实现接口如下:
[ApiVersion("2.0")]
void ITransactions.ExecuteTransaction(Executor executor) => this.Transaction("Anonymous transaction", executor);
[ApiVersion("2.0")]
void ITransactions.ExecuteInvisibleTransaction(Executor executor) => this.InvisibleTransaction(executor);
[ApiVersion("2.0")]
void ITransactions.ExecuteStickyTransaction(Guid guid, Executor executor) => this.StickyTransaction(guid, executor);
[ApiVersion("2.0")]
public INodeContext Context
{
[ApiVersion("2.0")] get => (INodeContext) this;
}
[ApiVersion("2.0")]
public ITransactions Transactions
{
[ApiVersion("2.0")] get => (ITransactions) this;
}
但仅通过事务公开 属性 而不是直接通过 DocumentNode。 我有一定控制权的代码有许多 class 继承自 DocumentNode 并过度使用 ExecuteTransaction 导致嵌套事务。 典型用途:
this.Transactions.ExecuteTransaction(...
Spotfire 内部有一些规则,规定当内部事务启动时外部事务可以处于什么状态,并且当状态不匹配时它会抛出异常。我无法访问该内部状态,但我会对这样的解决方案感到满意,如果捕获到此类异常,我们 运行 执行程序而不启动事务。 我还需要将代码更改保持在最低限度(我就是这么做的)。
所以我打算把一些从 DocumentNode 继承的 classes 改为从 DocumentNodeExt
继承public class DocumentNodeExt : DocumentNode
{
public DocumentNodeExt() : base()
{ }
public DocumentNodeExt(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
public new ITransactions Transactions
{
get { return this; }
}
public AggregatedTransactionHandle BeginAggregatedTransaction()
{
return base.Transactions.BeginAggregatedTransaction(); // stack overflow
// return ((DocumentNode)this).Transactions.BeginAggregatedTransaction(); // stack overflow
}
public void ExecuteInvisibleTransaction(Executor executor)
{
try
{
base.Transactions.ExecuteInvisibleTransaction(executor);
}
catch (InvalidOperationException)
{
executor();
}
}
public void ExecuteStickyTransaction(Guid guid, Executor executor)
{
base.Transactions.ExecuteStickyTransaction( guid, executor);
}
void ExecuteTransaction(Executor executor)
{
try
{
base.Transactions.ExecuteTransaction(executor);
}
catch (InvalidOperationException)
{
executor();
}
}
}
问题来了:因为 DocumentNode.Transactions 不是虚拟的,所以我无法在派生的 class 中覆盖它。我只能通过“新”隐藏它, 但是我该如何调用 DocumentNode 的 ExecuteTransaction 实现呢? base 没有方法 ExecuteTransaction 并且 base.Transactions 与 this.Transactions.
相同我可以忘记隐藏事务并简单地实现 ITransactions,但是我还需要更改来自
的典型调用this.Transactions.ExecuteTransaction(...
到
this.ExecuteTransaction(...
这比我的“配额”允许的代码更改更多。
您只能包装 ITransactions
成员。
public class DocumentNode : ITransactions
{
void ITransactions.ExecuteInvisibleTransaction() => Console.WriteLine("ExecuteInvisibleTransaction in DocumentNode");
public ITransactions Transactions => this;
}
public class DocumentNodeExt : DocumentNode
{
public new ITransactions Transactions { get; }
public DocumentNodeExt()
{
Transactions = new TransactionsWrapper(this);
}
}
public class TransactionsWrapper : ITransactions
{
private readonly ITransactions _transactions;
public TransactionsWrapper(ITransactions transactions)
{
_transactions = transactions;
}
public void ExecuteInvisibleTransaction()
{
Console.WriteLine("ExecuteInvisibleTransaction in TransactionsWrapper");
_transactions.ExecuteInvisibleTransaction();
}
}
public interface ITransactions
{
void ExecuteInvisibleTransaction();
}
https://dotnetfiddle.net/fzU3e1
注意 DocumentNodeExt
不继承接口,只有 ITransaction
成员的包装器继承。
继承与基础相同的接口class并调用基础成员是导致堆栈溢出的原因。
由于我无法理解为什么会发生此堆栈溢出,因此我为此打开了一个单独的问题
I could forget hiding Transactions and simply implement the ITransactions, but then I would also need to change the typical calls from
this.Transactions.ExecuteTransaction(...
to
this.ExecuteTransaction(...
我不明白为什么。让我们确定一些事情:
this
是从DocumentNode
. 继承的某种类型
DocumentNode
实施ITransaction
.Transactions
是非虚拟的。Transactions
在基数DocumentNode
中定义为(ITransaction) this
.
从 1.、2. 和 4. 可以看出,对于 DocumentNode
对 Transactions
的实施,以下始终为真:
this.ReferenceEquals(this.Transactions)
因为 4. 它必须在所有不隐藏 Transactions
的子类中为真,因为它们无法覆盖它。所以只要你没有在子类中做任何奇怪的隐藏,对 this.ExecuteTransaction
和 this.Transactions.ExecuteTransaction
的调用就必须相同。
要将其放入代码中,请考虑您的代码的这个精简版本:
public interface ITransactions
{
void ExecuteTransaction();
}
public class DocumentNode : ITransactions
{
void ITransactions.ExecuteTransaction() => Console.WriteLine("Base ExecuteTransaction");
public ITransactions Transactions => (ITransactions) this;
}
public class ExtDocumentNode : DocumentNode, ITransactions
{
public void ExecuteTransaction() => Console.WriteLine("Overriden ExecuteTransaction");
}
public class Implementation : DocumentNode
{
public void Foo() => this.Transactions.ExecuteTransaction();
}
public class ExtImplementation : ExtDocumentNode
{
public void Foo() => this.Transactions.ExecuteTransaction();
}
现在代码如下:
var instance = new Implementation();
var extInstance = new ExtImplementation();
instance.Foo();
extInstance.Foo();
打印
Base ExecuteTransaction
Overriden ExecuteTransaction
符合预期(参见 demo)。
因此,如前所述,我要么完全没有抓住要点,要么您的问题不是问题。如有不妥请指正