C# getters/setters 在结构和接口中
C# getters/setters in structs and interfaces
我发现了一个(据我所知)C# 中结构和接口之间的奇怪区别。考虑这个接口和结构:
public interface INumber
{
void ChangeNumber(int n);
void Log();
}
public struct Number : INumber
{
private int n;
public void ChangeNumber(int n)
{
this.n = n;
}
public void Log()
{
Console.WriteLine(this.n);
}
}
当我创建一个新的 class 并且数字为 属性 时,使用 ChangeNumber 方法将 n 更改为 2 并使用 Log 打印数字,它打印 0 而不是:
public class NumberContainer
{
public Number Number { get; set; }
public NumberContainer()
{
this.Number = new Number();
this.Number.ChangeNumber(2);
this.Number.Log(); //prints 0...
}
}
过了一会儿我意识到这是因为当我调用 this.Number.ChangeNumber(2);
时,我实际上创建了一个新对象(因为 getter)并将该数字更改为 2。但后来我更改了一个通过将 Number 属性 更改为 INumber 属性 的一点代码:
public class NumberContainer
{
public INumber Number { get; set; }
public NumberContainer()
{
this.Number = new Number();
this.Number.ChangeNumber(2);
this.Number.Log(); //prints 2!
}
}
在这种情况下,它会打印 2!为什么会这样?相同的结构原理是否适用于接口?
不同的是,struct
是作为值类型使用的,而interface
(可以用class或者结构体来实现)是引用类型。
这在您的示例中产生了巨大的差异。在第一种情况下,您在做什么调用 this.Number
意味着 "Get me the value of the number" - 这意味着它将值拉入堆栈,并且堆栈上的(未命名)变量(未存储在任何地方)被修改。
在另一种情况下,接口是引用类型——这意味着,它获取存储在其地址上的任何内容并对其进行修改。
一般来说,我不建议使用可变的 struct
(正如评论中已经提到的那样)。
您可以阅读有关此主题的更多信息,例如这里:Why are mutable structs “evil”?
这是由 NumberContainer
class 中的自动 属性 引起的,当您访问 属性.
时,您总是会得到一个值的副本
如果您将 属性 更改为一个字段,它将按预期工作。请记住,auto属性 只是一对方法,当 returned/passed to/from 任何方法时,值类型都会被复制。
当你打电话时
this.Number.ChangeNumber(2);
this.Number.Log(); //prints 0...
你实际上是在打电话:
this.getNumber() // returns copy of value type
.ChangeNumber(2); // executes op on that copy
this.getNumber()
.Log();
当您使用接口时,您将返回对对象的引用,因此操作总是在同一个对象上执行。
我发现了一个(据我所知)C# 中结构和接口之间的奇怪区别。考虑这个接口和结构:
public interface INumber
{
void ChangeNumber(int n);
void Log();
}
public struct Number : INumber
{
private int n;
public void ChangeNumber(int n)
{
this.n = n;
}
public void Log()
{
Console.WriteLine(this.n);
}
}
当我创建一个新的 class 并且数字为 属性 时,使用 ChangeNumber 方法将 n 更改为 2 并使用 Log 打印数字,它打印 0 而不是:
public class NumberContainer
{
public Number Number { get; set; }
public NumberContainer()
{
this.Number = new Number();
this.Number.ChangeNumber(2);
this.Number.Log(); //prints 0...
}
}
过了一会儿我意识到这是因为当我调用 this.Number.ChangeNumber(2);
时,我实际上创建了一个新对象(因为 getter)并将该数字更改为 2。但后来我更改了一个通过将 Number 属性 更改为 INumber 属性 的一点代码:
public class NumberContainer
{
public INumber Number { get; set; }
public NumberContainer()
{
this.Number = new Number();
this.Number.ChangeNumber(2);
this.Number.Log(); //prints 2!
}
}
在这种情况下,它会打印 2!为什么会这样?相同的结构原理是否适用于接口?
不同的是,struct
是作为值类型使用的,而interface
(可以用class或者结构体来实现)是引用类型。
这在您的示例中产生了巨大的差异。在第一种情况下,您在做什么调用 this.Number
意味着 "Get me the value of the number" - 这意味着它将值拉入堆栈,并且堆栈上的(未命名)变量(未存储在任何地方)被修改。
在另一种情况下,接口是引用类型——这意味着,它获取存储在其地址上的任何内容并对其进行修改。
一般来说,我不建议使用可变的 struct
(正如评论中已经提到的那样)。
您可以阅读有关此主题的更多信息,例如这里:Why are mutable structs “evil”?
这是由 NumberContainer
class 中的自动 属性 引起的,当您访问 属性.
如果您将 属性 更改为一个字段,它将按预期工作。请记住,auto属性 只是一对方法,当 returned/passed to/from 任何方法时,值类型都会被复制。
当你打电话时
this.Number.ChangeNumber(2);
this.Number.Log(); //prints 0...
你实际上是在打电话:
this.getNumber() // returns copy of value type
.ChangeNumber(2); // executes op on that copy
this.getNumber()
.Log();
当您使用接口时,您将返回对对象的引用,因此操作总是在同一个对象上执行。