简体   繁体   English

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

[英]C# “Rename” Property in Derived Class

When you read this you'll be awfully tempted to give advice like "this is a bad idea for the following reason..." 当你读到这篇文章时,你会非常想提出一些建议,比如“由于以下原因,这是一个坏主意......”

Bear with me. 忍受我。 I know there are other ways to approach this. 我知道还有其他方法可以解决这个问题。 This question should be considered trivia. 这个问题应该被认为是琐事。

Lets say you have a class "Transaction" that has properties common to all transactions such as Invoice, Purchase Order, and Sales Receipt. 假设您有一个“交易”类,它具有所有交易的共同属性,例如发票,采购订单和销售收据。

Let's take the simple example of Transaction "Amount", which is the most important monetary amount for a given transaction. 让我们以交易“金额”的简单示例为例,这是给定交易的最重要的货币金额。

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

    public TxnTypeEnum TransactionType { get; set; }
}

This Amount may have a more specific name in a derived type... at least in the real world. 此数量可能在派生类型中具有更具体的名称...至少在现实世界中。 For example, the following values are all actually the same thing: 例如,以下值实际上都是相同的:

  • Transaction - Amount 交易 - 金额
  • Invoice - Subtotal 发票 - 小计
  • PurchaseOrder - Total PurchaseOrder - 总计
  • Sales Receipt - Amount 销售收据 - 金额

So now I want a derived class "Invoice" that has a Subtotal rather than the generically-named Amount. 所以现在我想要一个派生类“Invoice”,它有一个小计而不是一般命名的Amount。 Ideally both of the following would be true: 理想情况下,以下两种情况都是正确的:

  1. In an instance of Transaction, the Amount property would be visible. 在Transaction的实例中,Amount属性将是可见的。
  2. In an instance of Invoice, the Amount property would be hidden, but the Subtotal property would refer to it internally. 在Invoice的实例中,Amount属性将被隐藏,但Subtotal属性将在内部引用它。

Invoice looks like this: 发票看起来像这样:

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

But of course Transaction.Amount is still visible on any instance of Invoice. 但当然,在任何Invoice实例上仍然可以看到Transaction.Amount。

Thanks for taking a look! 谢谢参观!

Thanks for all the help. 谢谢你的帮助。

OK, of course you cannot "hide" public properties on the base class when the derived class IS a base instance. 好吧,当派生类是基础实例时,当然不能“隐藏”基类上的公共属性。 Somewhere deep in my brain I already knew that. 在我脑海深处的某个地方,我已经知道了。 Doh! 卫生署!

I wound up getting the syntactic sugar to behave the way I wanted for the consumer by using a third class called TransactionBase. 通过使用名为TransactionBase的第三个类,我最终得到了语法糖,以表达我对消费者的需求。 This class is abstract and contains the shared, non-aliased stuff that exists for all transactions like currency, exchange rate, created/modified date and time, transaction date, etc... in addition to aliased stuff like Amount. 这个类是抽象的,包含所有交易的共享,非别名的东西,如货币,汇率,创建/修改日期和时间,交易日期等......除了Amount这样的别名之外。

Here, I just show the Amount property in question: 在这里,我只显示有问题的Amount属性:

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

Then Transaction looks like this: 然后Transaction看起来像这样:

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

And Invoice: 和发票:

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

And access works the way I wanted: 访问按照我想要的方式工作:

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;

So the answer to my original question was, "no" you cannot hide public inherited members. 所以我原来的问题的答案是,“不”你不能隐藏公共继承成员。 The above solution fakes it with accessing the protected member directly in the derived types, but it still sort of sucks. 上面的解决方案通过直接在派生类型中访问受保护的成员来伪造它,但它仍然很糟糕。 Too much typing. 打字太多了。

Of course, now that I fiddled and piddled with all that, I'm seeing that a better solution throws out the protected members altogether and saves me some typing. 当然,现在我摆弄和摆弄所有这些,我看到一个更好的解决方案完全抛弃了受保护的成员并节省了一些打字。 By the way, YES I am embarrassed that I didn't jump immediately to this solution. 顺便说一句,是的,我很尴尬,我没有立即跳到这个解决方案。

EDIT: Actually, the first appraoch in my answer might be better. 编辑:实际上,我的答案中的第一个appraoch可能会更好。 With the 2nd one, I'd lose the "Amount" or "Subtotal" when casting from a Transaction to an Invoice. 对于第二个,当从交易转换为发票时,我将失去“金额”或“小计”。

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

In short, you can't do this. 简而言之,你不能这样做。

In long, you can emulate this by coding to interfaces! 总之,您可以通过编码接口来模拟这一点!

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

The exact behavior you're looking for doesn't make sense syntactically. 你正在寻找的确切行为在语法上没有意义。 If Invoice inherits Transaction , then it is a kind of transaction, and the compiler requires it to inherit all of its properties. 如果Invoice继承Transaction ,那么它是一种事务,编译器要求它继承其所有属性。

The general behavior you're looking for is, I think, encapsulation, which can be accomplished with interfaces. 我认为,您正在寻找的一般行为是封装,可以通过接口来实现。

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

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

Require the consumers of your code to use these interfaces, and then the implementation details are hidden to them. 要求代码的使用者使用这些接口,然后隐藏实现细节。


So now, the classes can behave the way you want. 所以现在,这些类可以按照你想要的方式运行。 SubTotal will still be visible to the class (Invoice), but will be hidden to the interface (IInvoice). 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; }
    }
}

I would recommend using composition over inheritance in this instance. 在这个例子中,我建议使用组合而不是继承。 The main reason is that the base implementation of Transaction seems to possibly never be used in the way intended through inheritance. 主要原因是Transaction的基本实现似乎永远不会以通过继承的方式使用。 You can hide the transaction as a protected or private member of the Invoice and expose / manipulate it using the public properties of Invoice. 您可以将事务隐藏为Invoice的受保护或私有成员,并使用Invoice的公共属性公开/操作它。

One such example could look like: 一个这样的例子看起来像:

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

How about using an implicit conversion operator? 如何使用隐式转换运算符?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM