【问题标题】:Reference variable to an instance member引用变量到实例成员
【发布时间】:2018-09-30 03:24:08
【问题描述】:

我有这个数据类:

class MyClass
{
    Foo Foo1 { get; set; }
    Foo Foo2 { get; set; }
    Foo Foo3 { get; set; }
    Foo Foo4 { get; set; }
}

现在,在我程序的其他地方,我有这个功能:

void ModifyMyClass(MyClass myClassInstance)
{
    var rightFooToModify = myclassInstance.Foo1;

    rightFooToModify = new Foo();  // Here, my intention is to
                                   // modify MyClass, not just the
                                   // local variable 'rightFooToModify'
}

我需要能够选择 Foo1、Foo2、Foo3、Foo4 之一(取决于某些条件),然后将其设置为新的 Foo 对象。

实现这一目标的最佳方法是什么?我可以在 C# 中使用某种引用变量吗?

编辑:

我可以使用(实际上是我现在使用的):

myClassInstance.Foo1 = new Foo();

但是,ModifyMyClass() 其实要复杂一些,例如:

void ModifyMyClass(MyClass myClassInstance)
{
    if (someCondition)
    {
        var rightFooToModify = myClassInstance.Foo1
        // ... Do some computation with rightFooToModify
        myClassInstance.Foo1 = new Foo(/* some parameters */);
    }
    else if (someOtherCondition)
    {
        // The exact same code as the if above , but with
        // myClassInstance.Foo2 instance.
    }
}

我想避免在以下所有else if 中重复if 块中的代码。这就是为什么我只想在方法的开头使用var rightFooToModify = myClassInstance.Foo1,而不是在多个ifs 中不必要地复制代码。

【问题讨论】:

  • myclassInstance.Foo1 = new Foo()
  • 这就是我所做的。我编辑了我的问题以说明我为什么要寻找更好的方法。
  • 您为什么不提取一个选择器方法,根据您的条件,只需将正确的Foo 实例提供给您的ModifyMyClass 方法。这将节省一些重复。然后,您可以根据代码库的外观进行更多重构(例如,如果所有这些 Foo 实例略有不同,您可以尝试设置类层次结构或使用泛型)。但我想说分离选择器方法是开始。
  • 我认为真正的问题(导致重复)更多的是分配部分。即使使用选择器,如果我想分配给实例,无论如何我都必须使用名称myClassInstance.Foo1 = ...。

标签: c# reference


【解决方案1】:

你可以这样想

当您将属性分配给局部变量时,

var rightFooToModify = myclassInstance.Foo1;

你所做的只是写一个便条,上面写着一些记忆的位置。

如果有人给你另一张便利贴,里面有更多内容,请替换原来的便利贴

rightFooToModify = new Foo();

它并没有改变原来的记忆,它还在,什么也没发生。

这就是指针/引用的工作方式。他们指向/引用内存中的一个位置,您可以复制一个引用,但覆盖该引用不会对原始内存位置做任何事情,您只是有一个新的便利贴指向其他地方。

但是,现在提供一个非常人为的示例,说明如何使用 ref return 和 ref Local 克服这个问题

免责声明,我真的不提倡在您的情况下这样做,但是出于纯粹的学术目的,让我们看看这种语言 功能

给定

public class MyClass
{
   private Foo _foo1;

   public Foo Foo1
   {
      get => _foo1;
      set => _foo1 = value;
   }

   public ref Foo ModifyMyClass()
   {
      return ref _foo1;
   }
}

示例

// create class
var someClass = new MyClass();

// create some memory for it
someClass.Foo1 = new Foo();
someClass.Foo1.SomeValue = "test";

// copy the reference to it
var copy = someClass.Foo1; 

// overwrite it
copy = new Foo();
copy.SomeValue = "test2";

// oh nossssssssss!!!! is didn't change!!
Console.WriteLine(someClass.Foo1.SomeValue);


//enter ref local
ref var  actualLocation = ref someClass.ModifyMyClass();

// create some memory
actualLocation = new Foo();
actualLocation.SomeValue = "test3";

// omg it worked!!!!
Console.WriteLine(someClass.Foo1.SomeValue);

输出

test
test3

还有另一种奇怪的 ref 用法

someClass.ModifyMyClass() = new Foo();
someClass.ModifyMyClass().SomeValue = "I hope i never see this in my code base";
Console.WriteLine(someClass.Foo1.SomeValue);

【讨论】:

    【解决方案2】:

    我不知道这是否有任何帮助,但你可以有一个方法返回 Action<MyClass, Foo> 类型的正确设置器,即

    Action<MyClass, Foo> GetFooSetter()
    {
        if (someCondition)
        {
            return (myClass, foo) => myClass.Foo1 = foo;
        }
        else if (someOtherCondition)
        {
            return (myClass, foo) => myClass.Foo2 = foo;
        }
    }
    

    然后这样称呼它:

    GetFooSetter()(myClassInstance, new Foo());
    

    【讨论】:

      【解决方案3】:

      用例不是很清楚,所以我建议两个选项:

      1. 如果Foo 不是不可变的,那么只需修改它的值而不是将其分配给新实例。你甚至可以添加一个复制功能来复制另一个Foo的值:

        Foo fooToChange = myClassInstance.Foo1;
        fooToChange.CopyFrom(new Foo());
        
      2. 重构以将 Foos 保存在一个数组中,然后您可以请求它并使用索引进行分配:

        class MyClass
        {
            public Foo[] Foos = new Foo[4];
        }
        
        int fooToChange = 3;
        myClassInstance.Foos[3] = new Foo();
        

      【讨论】:

        猜你喜欢
        • 2020-01-14
        • 1970-01-01
        • 1970-01-01
        • 2018-11-20
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多