Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot pass 'this' as a ref or out argument because it is read-only

Tags:

c#

My class that passes itself to a method in another class. This external method (the last line in the code below) alters the object I’m passing accordingly, returns control, and continues on its merry way. But the last line ThisPaymentGateway.Process(ref this); says this at design time:

Cannot pass 'this' as a ref or out argument because it is read-only***

There are no read-only properties there. How can I resolve this?

using System;

namespace Something
{
  namespace Finance
  {
    namespace Donations
    {
      public class Electronic1 : Donation
      {
        private PaymentGateway ThisPaymentGateway { get; set; } = new PaymentGateway();

        public void RunController()
        {
          if (DonorType.ToUpper() == "IND" && (PaymentMethod.ToUpper() == "CREDIT CARD" || PaymentMethod.ToUpper() == "ECHECK"))
          {
            ThisPaymentGateway.Process(ref this);
          }
        }
      }
    }
  }
}
like image 662
TRH Avatar asked Sep 08 '16 21:09

TRH


2 Answers

Remove "ref" from the call and Process method signature

If you just want to "alter the object" and then return, you don't need to pass it by ref anyway. Classes are already reference types and therefore if you change the object properties in Process it will change them in the source object.

like image 60
Scott Perham Avatar answered Nov 09 '22 22:11

Scott Perham


First of all do not use strings instead of enum, it may not be obvious but you're performing culture aware comparison and you're converting PaymentMethod to uppercase using current culture rules. What does it mean? Schoolbook example is Turkish locale: "ind".ToUpper() != "IND". If you need to compare two strings regardless case use String.Equals(PaymentMethod, "IND", StringComparer.InvariantCultureIgnoreCase) (here I assume, because one string is hard-coded that you want to perform comparison using invariant culture rules but also ordinal comparison may be appropriate).

In C# you do not need to declare namespace in that way, this syntax is valid:

namespace Something.Finance.Donations {
}

About your question: variables passed with ref may be changed. this (let's imagine it's a variable, just imagine...) obviously cannot be changed then you have that compiler error.

Read this:

static void ChangeAnimalWithRef(ref Animal animal) {
    animal = new Cat();
    Debug.Assert(animal.GetType() == typeof(Cat));
}

static void ChangeAnimalWithoutRef(Animal animal) {
    animal = new Cat();
    Debug.Assert(animal.GetType() == typeof(Cat));
}

void Test1() {
    Animal animal = new Dog();
    Debug.Assert(animal.GetType() == typeof(Dog));

    ChangeAnimalWith(ref animal);    
    Debug.Assert(animal.GetType() == typeof(Cat));
}

void Test2() {
    Animal animal = new Dog();
    Debug.Assert(animal.GetType() == typeof(Dog));

    ChangeAnimalWithoutRef(animal);    
    Debug.Assert(animal.GetType() == typeof(Dog));
}

Think them as a pointer to a variable instead of the value of that variable (in C# parameters are passed by value, you need ref and out to pass them by reference).

If you own ThisPaymentGateway.Process() method I'd bet you just do not need ref modifier and you're just used to C++ references with &. In C# things are different.

If, for any obscure reason, you do not own that method and its signature cannot be changed then you have two options:

1) Create a dummy variable:

Electronic1 dummy = this;
ThisPaymentGateway.Process(ref dummy);
Debug.Assert(Object.ReferenceEquals(dummy, this), "Ooops, what are we supposed to do?");

2) Call that function from outside the class (and change your logic little bit):

var obj = new Electronic1();
if (obj.CanRunController())
    obj.ThisPaymentGateway.Process(ref obj);

I strongly doubt you need any of them, it's just for completeness.

like image 36
Adriano Repetti Avatar answered Nov 09 '22 21:11

Adriano Repetti