3

I'm making an "adapter" base class that instantiates a private struct. The struct is exposed to inheritors via an abstract Configure() method, so they can set properties on it. Implementation as follows:

public abstract class PaymentAdapter {

    private PaymentObject p = new PaymentObject();

    protected PaymentObject CreditCardPayment {
        get { return p; }
    }

    protected abstract void Configure(PaymentObject payment);

    public MyResponse ProcessPayment() {
        // Run the adapter's setup
        Configure(p);

        // Charge the customer
        var chargeResult = p.DoSomething();

        return new MyResponse {
            MyResult = chargeResult
        };
    }
}

Those of you who are observant will see what the following line needs some attention:

protected abstract void Configure(PaymentObject payment);

When overridden in a concrete class, this method (almost) gives the consumer the opportunity to modify the struct's properties directly. This is the desired result.

My question is - should I use a ref argument, or change the void to PaymentObject, making the consumer return an instance themselves?

Method 1:

protected abstract PaymentObject Configure(PaymentObject payment);

Method 2:

protected abstract void Configure(ref PaymentObject payment);

So, when inheriting the class, the consumer would have to do the following:

Method 1:

public class MyConsumer : PaymentAdapter {
    #region Overrides of PaymentAdapter

    protected override PaymentObject Configure(PaymentObject payment) {
        payment.AProperty = "Something";
            return payment;
    }

    #endregion
}

Method 2:

public class MyConsumer : PaymentAdapter {
    #region Overrides of PaymentAdapter

    protected override void Configure(ref PaymentObject payment) {
        payment.AProperty = "Something";
    }

    #endregion
}

Apart from the slight change in syntax, are there any other differences at all? Is this a preference thing, or are there benefits I can't see to using one over the other?

As there's slightly less code, I would be inclined to use the "ref" method, contrary to all my years of exclusively returning objects from methods. This seems like a perfect case for a ref argument - it makes the consumer's job slightly easier, and means I'm not setting objects all over the place.

Spikeh
  • 3,540
  • 4
  • 24
  • 49
  • 5
    In Method 2 you don't need a `ref` parameter; `PaymentObject` is a class, not a a struct (I presume), and is therefore already a reference type. – antonijn Feb 20 '13 at 12:54
  • OK, let's say PaymentObject is a struct then :) – Spikeh Feb 20 '13 at 12:57
  • I suggest you use the return method. There is no actual need to use a ref, you don't have several objects you want to return. I wouldn't care about 2 or 3 lines of code more or less. – bash.d Feb 20 '13 at 12:57
  • @bash.d But if it's a big struct, it might cause performance issues (first copying the object to the parameter and then copying it to the return type). – antonijn Feb 20 '13 at 12:58
  • @Antonijn True, but how big is big? If that is the issue, I agree, apart from that, you should favor return over ref. – bash.d Feb 20 '13 at 13:00

4 Answers4

2

Reference types are passed by reference by default, so you don't have to use ref keyword here.

If your type is a Struct you should definitely use return statement, because structures should be immutable (why? Read answers for Why are mutable structs “evil”? and Why are C# structs immutable?)

Community
  • 1
  • 1
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
1

If PaymentObject is a struct, then method 2 will be faster, since in method 1, you first copy an instance of PaymentObject, modify that copy, and return a copy of that copy. In a benchmark I did (for a struct 64 bytes large, Mono on Linux Mint 14) the second method is up to three times as fast.

However, there are some drawbacks to the second method. In C#, there is kind of an unwritten rule, where you shouldn't directly mutate a struct not belonging to the method or class in question. This mainly has to do with threading and complications concerning interfaces.

So I would go for the first method, unless you really want performance and don't need to worry about complications with threading.

antonijn
  • 5,702
  • 2
  • 26
  • 33
  • That's exactly what I thought - if I used a "return", I would effectively end up with 3 copies of the struct in memory... though it's a cleaner way to do things. I'm actually modifying anywhere between 5 and 25 properties on the PaymentObject. – Spikeh Feb 20 '13 at 13:20
0

If you are only changing properties on the actual PaymentObject there is no need for using ref. It will still be the same object that is changed.

I would suggest you go with

protected abstract void Configure(PaymentObject payment);

There is not need to return the object since its the actual object that will be changed.

Evelie
  • 2,919
  • 2
  • 14
  • 21
0

I think you should go for 'ref' parameter in this case. Because using 'ref' you can get the PaymentObject value from 'CreditCardPayment' property also. But if you return the the object then it will return the object only inside 'ProcessPayment' method and you can not get the actual value from the 'CreditCardPayment' property.

Arnab
  • 454
  • 1
  • 4
  • 14