【问题标题】:How IConfiguration.Bind() Works In .NET Core without 'out' or 'ref' in it's Arguments?IConfiguration.Bind() 如何在没有“out”或“ref”的情况下在 .NET Core 中工作?
【发布时间】:2022-01-15 04:42:10
【问题描述】:

在 .NET Core 中,我们使用 IConfiguration 将配置绑定到实例。

var settings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(settings);

为了在我的个人项目中创建类似 Bind() 的函数(针对不同的需求),我创建了这样的方法

public void BindOption(ref object instance)
{
  ...
}

并像这样使用它

var settings = new AppSettings();
MyCustomStaticClass.BindOption(ref settings);

现在我的问题是

Bind() 函数如何在其参数中不明确提及 outref 的情况下工作?

【问题讨论】:

  • 问问自己这个问题:“为什么在调用Bind() 之前必须先做var settings = new AppSettings();?尤其是new AppSettings() 部分。
  • 我的理解是 bind 期望一个对象的实例而不是对象本身。表示预初始化的对象。如果我的理解有误,请纠正我。
  • 除了使用“out”或“ref”之外,我能想到的唯一方法是依靠反射来进行一般处理,这是我需要在这里确认的
  • 这是不存在的区别。一个实例一个“对象本身”。是的,它使用反射来填充你给它的实例的属性。如果Bind 负责创建实例并将其返回给您,您只需要out,但事实并非如此;您创建了实例,因此您不需要它来修改您的变量。

标签: c# .net-core configuration appsettings


【解决方案1】:

听起来您误解了ref/out - 当您使用传递给方法的引用类型(类)时,他们为您做的唯一一件事就是允许该方法分配一个新的将对象实例传递给参数变量,调用代码会看到更改。它们绝对不需要更改传递变量的某些数据内容。

我个人认为围绕这​​个问题的很多困惑来自于“通过引用传递”这个短语,其中包含一个以ref 开头的单词,巧合的是ref 中的三个字母

当你创建一个引用类型实例时:

Person john = new Person(){ Name = "John", Age = 27 };

你可以把它传递给一个方法:

void ChangeName(Person p){
  p.Name = "Fred";
}

您无需指定 ref 即可更改此名称并将其保留

ChangeName(john);
Console.Print(john.Name); //Fred

在 C# 内部,假设您创建 John 并且 C# 分配一些内存,将名称放入其中并为您提供变量引用:

john --> { Name: "John", Age: 27 }

您调用该方法,而 C# 暂时创建另一个名为 p 的引用(因为该方法需要一个人 p)

john --> { Name: "John", Age: 27 } <-- p

新的引用,相同的对象。您通过 p.Name 更改名称,原始对象受到影响

john --> { Name: "Fred", Age: 27 } <-- p

如果我们使用ref C# 没有创建新的引用,想象它只是暂时重命名我们现有的引用,然后在方法完成时将其重新命名

void ChangeName(ref Person p){
  p.Name = "Fred";
}


john --> { Name: "John", Age: 27 }      //start with
p --> { Name: "John", Age: 27 }          //when start calling the method 
p --> { Name: "Fred", Age: 27 }          //method changes the person name
john --> { Name: "Fred", Age: 27 }      //method exits, name is changed

我们在这里从来不需要参考


不同的场景。人是不可变的。更改名称的唯一方法是将其换成一个全新的对象

void ChangeName(Person p){
  p = new Person { Name = "Fred", Age: 0 };
}

现在,this 需要参考。看看当我们没有 ref 时会发生什么:

john --> { Name: "John", Age: 27 }
john --> { Name: "John", Age: 27 } <-- p
john --> { Name: "John", Age: 27 }     p --> { Name: "Fred", Age: 0 }
john --> { Name: "John", Age: 27 }

调用该方法会为旧对象创建一个新引用,然后说 p = new... 会将该新引用重新附加到某个新对象,该新对象会在方法结束时立即销毁,没有任何变化

带参考:

john --> { Name: "John", Age: 27 }
p --> { Name: "John", Age: 27 } 
p --> { Name: "Fred", Age 0 }
john --> { Name: "Fred", Age 0 }

临时重命名该引用意味着当我们创建一个新对象并分配它时,原始调用代码也会遇到new


仅当您将传入的事物设置为 new 并希望调用者看到新事物时,您才需要 ref。大多数时候我们不需要它

脚注:outref 类似,只是编译器会检查该方法是否确实分配了一个值。将变量传递给方法时,您需要使用“out”或“ref”,因为它会引起您的注意“此方法可能会覆盖您传入的任何内容”(对于 ref)或“此方法肯定会覆盖您的任何内容传入”(不传)

现代 .net 还具有很少使用的 in 修饰符,它使编译器检查您没有将任何 new 分配给传入的变量(存在非引用场景的风险,其中Fred在方法结束时丢失)

【讨论】:

  • 嘿,Caius,我明白了。但是我对您的答案的第三个代码块感到困惑-“您无需指定 ref 即可更改此名称并使其保持不变”。据我了解,这对于全局变量是正确的,但不是相反。我的印象是按值传递只会在函数内创建一个操作范围副本
  • 当我们没有明确指定 ref 时,它会接受 arg 'by value' 而不是 'by reference' 对吗?在这种情况下,如果您更改该有趣范围“ChangeName(Person p)”内的 var,除非它是全局 var,否则它将对外部的原始 var 没有影响。这是我的理解。
  • 参考:youtu.be/lYdcY5zulXA?t=102 - 如果我错了,请纠正我
  • 人们认为“按值传递意味着复制”和“按引用传递意味着原始”,这是真的,但在引用类型(类)的情况下,它是 指针那是复制的,而不是数据。无论您有复制的指针还是原始指针,data 仍然是相同的一个数据,因此您可以在任何一种情况下更改数据。复制的指针唯一不能做的就是影响原始指针指向的位置。如果您说x = new...,则复制的指针小马指向内存中其他地方的新数据,而原始指针仍指向原始数据
  • 好吧,这是有道理的,因为我们正在讨论引用类型,而视频显示了值类型。明白你的意思了!
猜你喜欢
  • 2017-10-21
  • 2020-06-09
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多