C#“重命名”派生类中的属性

时间:2012-09-07 21:28:17

标签: c# properties alias derived-class base-class

当你读到这篇文章时,你会非常想提出一些建议,比如“由于以下原因,这是一个坏主意......”

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

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

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

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

    public TxnTypeEnum TransactionType { get; set; }
}

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

  • 交易 - 金额
  • 发票 - 小计
  • PurchaseOrder - 总计
  • 销售收据 - 金额

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

  1. 在Transaction的实例中,Amount属性将可见。
  2. 在Invoice的实例中,Amount属性将被隐藏,但Subtotal属性将在内部引用它。
  3. Invoice看起来像这样:

    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; }
    }
    

    但当然,在任何Invoice实例上仍然可以看到Transaction.Amount。

    谢谢你看看!

5 个答案:

答案 0 :(得分:6)

感谢您的帮助。

好的,当派生类是基础实例时,当然不能“隐藏”基类的公共属性。在我脑海深处的某个地方,我已经知道了。卫生署!

我通过使用名为TransactionBase的第三个类,让语法糖以我想要的方式表现出来。这个类是抽象的,包含所有交易的共享,非别名的东西,如货币,汇率,创建/修改日期和时间,交易日期等......除了Amount这样的别名之外。

在这里,我只显示有问题的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;

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

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

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

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; }
}

答案 1 :(得分: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;
        }
    }
}

答案 2 :(得分:1)

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

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

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

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

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


现在,这些类可以按照您想要的方式运行。 SubTotal 仍然可以在(发票)中看到,但会隐藏在界面(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; }
    }
}

答案 3 :(得分: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; }
    }

答案 4 :(得分: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
        };
    }
}