11

これを読むと、「これは次の理由で悪い考えです...」などのアドバイスをしたくなるでしょう。

我慢してください。これにアプローチする他の方法があることを私は知っています。この質問は雑学と考えるべきです。

Invoice、Purchase Order、Sales Receipt など、すべてのトランザクションに共通のプロパティを持つクラス「Transaction」があるとします。

トランザクション「金額」の簡単な例を見てみましょう。これは、特定のトランザクションで最も重要な金額です。

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

    public TxnTypeEnum TransactionType { get; set; }
}

この Amount は、派生型でより具体的な名前を持つ場合があります...少なくとも現実の世界では。たとえば、次の値はすべて実際には同じものです。

  • 取引金額
  • 請求書 - 小計
  • 注文書 - 合計
  • 売上領収書 - 金額

そこで、一般的な名前の Amount ではなく、Subtotal を持つ派生クラス "Invoice" が必要になりました。理想的には、次の両方が真になります。

  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と呼ばれる3番目のクラスを使用して、構文糖衣を消費者が望むように動作させることにしました。このクラスは抽象的であり、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;

ですから、私の最初の質問に対する答えは、「いいえ」ということでした。公開された継承されたメンバーを隠すことはできません。上記の解決策は、派生型で保護されたメンバーに直接アクセスすることでそれを偽造しますが、それでも一種の問題です。タイピングが多すぎます。

もちろん、私がそれらすべてをいじくり回したので、より良い解決策が保護されたメンバーを完全に捨てて、私にいくつかのタイピングを節約するのを見ています。ちなみに、はい、私はこの解決策にすぐにジャンプしなかったことを恥ずかしく思います。

編集:実際には、私の答えの最初のアプローチの方が良いかもしれません。2つ目では、トランザクションから請求書にキャストするときに「金額」または「小計」が失われます。

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 のパブリック プロパティを使用して公開/操作することができます。

そのような例の 1 つが次のようになります。

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 に答える