从基 class C# 获取隐藏成员

Getting hidden member from base class C#

在 TIBCO Spotfire 中(我无法控制他们的代码),有一个抽象的 class - DocumentNode 有一个 属性 像这样实现的事务(根据反汇编):

public ITransactions Transactions
{
  [ApiVersion("2.0")] get => (ITransactions) this;
} 

那么Doc​​umentNode实现

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(... 

我不明白为什么。让我们确定一些事情:

  1. this 是从 DocumentNode.
  2. 继承的某种类型
  3. DocumentNode 实施 ITransaction.
  4. Transactions 是非虚拟的。
  5. Transactions 在基数 DocumentNode 中定义为 (ITransaction) this.

从 1.、2. 和 4. 可以看出,对于 DocumentNodeTransactions 的实施,以下始终为真:

this.ReferenceEquals(this.Transactions)

因为 4. 它必须在所有不隐藏 Transactions 的子类中为真,因为它们无法覆盖它。所以只要你没有在子类中做任何奇怪的隐藏,对 this.ExecuteTransactionthis.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)。

因此,如前所述,我要么完全没有抓住要点,要么您的问题不是问题。如有不妥请指正