11

当你读到这篇文章时,你会非常想给出这样的建议:“这是一个坏主意,原因如下......”

忍受我。我知道还有其他方法可以解决这个问题。这个问题应该被认为是琐事。

假设您有一个“交易”类,它具有所有交易共同的属性,例如发票、采购订单和销售收据。

让我们以交易“金额”为例,它是给定交易最重要的货币金额。

public class Transaction
{
    public double Amount { get; set; }

    public TxnTypeEnum TransactionType { get; set; }
}

此金额在派生类型中可能具有更具体的名称……至少在现实世界中。例如,以下值实际上都是相同的:

  • 交易 - 金额
  • 发票 - 小计
  • 采购订单 - 总计
  • 销售收据 - 金额

所以现在我想要一个派生类“发票”,它有一个小计而不是一般命名的金额。理想情况下,以下两种情况都是正确的:

  1. 在 Transaction 的实例中,Amount 属性将是可见的。
  2. 在 Invoice 的实例中,Amount 属性将被隐藏,但 Subtotal 属性将在内部引用它。

发票长这样:

public class Invoice : Transaction
{
    new private double? Amount
    {
        get
        {
            return base.Amount;
        }
        set
        {
            base.Amount = value;
        }
    }

    // This property should hide the generic property "Amount" on Transaction
    public double? SubTotal
    {
        get
        {
            return Amount;
        }
        set
        {
            Amount = value;
        }
    }

    public double RemainingBalance { get; set; }
}

但当然 Transaction.Amount 在任何 Invoice 实例上仍然可见。

谢谢参观!

4

5 回答 5

7

感谢所有的帮助。

好的,当然,当派生类是基实例时,您不能“隐藏”基类上的公共属性。在我大脑深处的某个地方,我已经知道这一点。嗬!

通过使用名为 TransactionBase 的第三个类,我最终让语法糖按照我想要的方式为消费者提供服务。此类是抽象的,包含所有交易(如货币、汇率、创建/修改的日期和时间、交易日期等)存在的共享的、非别名的东西……除了诸如金额之类的别名之外。

在这里,我只显示有问题的 Amount 属性:

public abstract class TransactionBase
{
    protected virtual double Amount { get; set; }
}

然后事务看起来像这样:

public class Transaction : TransactionBase
{
    public new double Amount 
    { 
        get
        {
            return base.Amount;
        }
        set
        {
            base.Amount = value;
        }
    }
}

和发票:

public class Invoice : TransactionBase
{
    public double SubTotal
    {
        get
        {
            return Amount;
        }
        set
        {
            Amount = value;
        }
    }
}

并且访问按我想要的方式工作:

var transaction = new Transaction();

// This line works fine:
var transactionAmount = transaction.Amount;



var invoice = new Invoice();

// This line works fine:
var invoiceSubtotal = invoice.SubTotal;

// This line won't compile.
// Error: TransactionBase.Amount is inaccessible due to its protection level.
var invoiceAmount = invoice.Amount;

所以我最初的问题的答案是,“不”你不能隐藏公共继承成员。上面的解决方案通过直接在派生类型中访问受保护的成员来伪造它,但它仍然有点糟糕。打字太多。

当然,既然我摆弄了所有这些,我看到一个更好的解决方案完全抛弃了受保护的成员并节省了我一些打字时间。顺便说一句,是的,我很尴尬,我没有立即跳到这个解决方案。

编辑:实际上,我回答中的第一个方法可能会更好。对于第二个,从交易转换为发票时,我会丢失“金额”或“小计”。

public abstract class TransactionBase
{
    // There are some shared properties here.
}

public class Transaction : TransactionBase
{
    public double Amount { get; set; }
}

public class Invoice : TransactionBase
{
    public double SubTotal { get; set; }
}
于 2012-09-07T22:21:42.913 回答
3

简而言之,你不能这样做。

总而言之,您可以通过对接口进行编码来模拟这一点!

public class Transaction
{
    public double Amount { get; set; }
}
public interface IInvoice
{
    public double? SubTotal { get; set; }
}
public class Invoice : Transaction, IInvoice
{
    public double? SubTotal
    {
        get
        {
            return Amount;
        }
        set
        {
            Amount = value ?? 0.0f;
        }
    }
}
于 2012-09-07T21:35:48.580 回答
1

您正在寻找的确切行为在语法上没有意义。如果Invoice继承了Transaction,那么它就是一种事务,编译器要求它继承它的所有属性。

我认为您正在寻找的一般行为是封装,它可以通过接口来完成。

public interface IInvoice
{
    double? Amount { get; set; }
}

public interface ITransaction
{
    double? SubTotal { get; set; }
}

要求代码的使用者使用这些接口,然后对他们隐藏实现细节。


所以现在,这些类可以按照你想要的方式运行。 SubTotal对(Invoice)仍然可见,但对界面(IInvoice) 是隐藏的。

public class Transaction : ITransaction
{
    public double? SubTotal { get; set; }
}

public class Invoice : IInvoice
{
    public double? Amount
    {
        get { return base.SubTotal; }
        set { base.SubTotal = value; }
    }
}
于 2012-09-07T21:39:24.007 回答
1

在这种情况下,我建议使用组合而不是继承。主要原因是 Transaction 的基本实现似乎永远不会以继承的方式使用。您可以将交易隐藏为 Invoice 的受保护或私有成员,并使用 Invoice 的公共属性公开/操作它。

一个这样的示例可能如下所示:

public class Invoice
{
        private readonly Transaction _transaction; 
        public Invoice():this(new Transaction())
        {
        }
        public Invoice(Transaction transaction)
        {
            _transaction = transaction;
        }

        // This property should hide the generic property "Amount" on Transaction
        public double? SubTotal
        {
            get
            {
                return _transaction.Amount;
            }
            set
            {
                _transaction.Amount = value ?? 0.0f;
            }
        }

        public double RemainingBalance { get; set; }
    }
于 2012-09-07T21:41:57.453 回答
0

使用隐式转换运算符怎么样?

class Program
{
    static void Main(string[] args)
    {
        var transaction = new Transaction();
        var transactionAmount = transaction.Amount;

        var invoice = new Invoice();
        var invoiceSubTotal = invoice.SubTotal;

        Transaction fromInvoiceToTrans = invoice;
        var fromInvoiceToTransAmount = fromInvoiceToTrans.Amount;

    }
}

public class Transaction
{
    public decimal Amount {get; set;}
}

public class Invoice
{
    public decimal SubTotal
    {
        get;
        set;
    }

    public static implicit operator Transaction(Invoice invoice)
    {
        return new Transaction
        {
            Amount = invoice.SubTotal
        };
    }
}
于 2012-09-07T22:10:09.510 回答