无法将'this'作为ref或out参数传递,因为它是只读的。

6

我有一个类,该类将自身传递给另一个类的方法。这个外部方法(下面代码中的最后一行)会相应地更改我传递的对象,然后返回控制并继续进行工作。但是最后一行 ThisPaymentGateway.Process(ref this); 在设计时会显示以下内容:

无法将“this”作为ref或out参数传递,因为它是只读的***

那里没有只读属性。我该如何解决这个问题?

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

你如何调用 RunController() 函数? - Tigran
由于这段代码无法编译,我还未编写调用代码。 - TRH
1
Process需要通过ref传递参数吗?错误信息是因为无法重新分配this。如果您需要使用ref传递,您需要创建一个新变量并传递它。 - Lee
你是否有C++的背景?因为这是我从C++转来时遇到困难的概念。在C#中,所有类都是通过引用传递的,所以你尝试做的大多数事情只需要使用ThisPaymentGateway.Process(this)就可以实现。 - TJ Rockefeller
2个回答

8

从调用和Process方法签名中删除"ref"

如果您只想“修改对象”然后返回,那么无论如何都不需要通过引用传递它。类已经是引用类型,因此如果您在Process中更改对象属性,则会在源对象中更改它们。


好的,我来自VB.NET背景,在方法签名中每个参数都必须定义为ByVal或ByRef。你是在告诉我C#中默认是按引用传递(其中传递的对象本身被改变)吗?我不是在抱怨,但我想知道他们为什么还要那个关键字。 - TRH
在C#中,默认情况下,类通过引用传递,而结构体通过值传递 - 传递结构体的引用允许您更改原始对象内容,传递类的引用允许您更改实际引用...但不是_this_ :P - Scott Perham
只是提醒一下:在VB.NET中,情况完全相同。参数默认为ByVal(可选),只有当您想要通过引用传递它们时才需要添加ByRef。 - Adriano Repetti
5
请注意,引用类型(类)默认情况下也是按值传递的;只不过该值是对象的引用(所以看起来像“按引用传递”)。使用refout按引用传递引用类型实际上是传递“变量”,允许您替换对象为其他内容(而不仅仅是变异对象)。有关此主题的详细信息,请参见Jon Skeet的文章 - poke
如果这是真的,即使您删除了“ref”,代码也无法工作。在这里,参数通过引用传递是必要的。声称它仍然是通过引用传递的将会说代码不应该编译,原因与问题中的代码不编译完全相同。您的陈述完全矛盾和混乱。除了是谎言之外,它什么都不简单。 - Servy
显示剩余2条评论

2
首先,不要使用字符串代替enum。这可能并不明显,但您正在执行基于文化的比较,并且正在使用当前文化规则将PaymentMethod转换为大写。这是什么意思?土耳其语区域设置是教科书般的例子:"ind".ToUpper() != "IND"。如果您需要比较两个字符串而不考虑大小写,请使用String.Equals(PaymentMethod, "IND", StringComparer.InvariantCultureIgnoreCase)(在这里,我假设因为一个字符串是硬编码的,您想要使用固定的文化规则进行比较,但也可以使用序数比较)。
在C#中,您不需要以那种方式声明命名空间,这种语法是有效的:
namespace Something.Finance.Donations {
}

关于你的问题:使用ref传递的变量可能会被修改。但是,如果这个变量是不可变的(假设它是一个变量,只是想象一下...),那么就会出现编译器错误。
请阅读以下内容:
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));
}

将其视为变量的指针,而不是该变量的值(在C#中,参数按值传递,需要使用refout来按引用传递它们)。
如果您拥有ThisPaymentGateway.Process()方法,我敢打赌您不需要ref修饰符,并且您只是习惯于使用C++中的引用和&。在C#中情况不同。
如果由于某种晦涩的原因,您不拥有该方法,并且其签名无法更改,则有两个选项:
1)创建一个虚拟变量:
Electronic1 dummy = this;
ThisPaymentGateway.Process(ref dummy);
Debug.Assert(Object.ReferenceEquals(dummy, this), "Ooops, what are we supposed to do?");

2)从类外部调用该函数(并稍微更改逻辑):

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

我强烈怀疑你需要它们中的任何一个,这只是为了完整性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接