我需要一个C#中类似C/C++的指针的替代方案,这样我就可以存储构造函数中传递的变量的引用。我希望我的本地指针在每次引用值更改时都更改其值。就像一个指针。但是我不想在C#中使用真正的指针,因为它们是不安全的。有变通办法吗?
class A
{
public Int32 X = 10;
}
class B
{
public B(Int32 x)
{
Y = x;
}
public Int32 Y { get; set; }
}
static void Main(string[] args)
{
A a = new A();
B b = new B(a.X);
Console.WriteLine(b.Y); // 10
a.X = 11;
Console.WriteLine(b.Y); // 10, but I want it to be 11
}
在C#中编码时忘记指针并开始用C#思考:D
我会这样做:
public interface IXProvider
{
int X {get;}
}
class A : IXProvider
{
public int X {get; set;} = 10;
}
class B
{
public B(IXProvider x)
{
_x = x;
}
private readonly IXProvider _x;
public int Y => _x.X;
}
static void Main(string[] args)
{
A a = new A();
B b = new B(a);
Console.WriteLine(b.Y); // 10
a.X = 11;
Console.WriteLine(b.Y); // 11
}
位图示例:(为了简单起见,假设"SomeBitmap"one_answers"AnotherBitmap"为实际位图(
public interface IBitmapProvider
{
Bitmap X {get;}
}
class A : IBitmapProvider
{
public Bitmap X {get; set;} = SomeBitmap;
}
class B
{
public B(IBitmapProvider x)
{
_x = x;
}
private readonly IBitmapProvider _x;
public Bitmap Y => _x.X;
}
static void Main(string[] args)
{
A a = new A();
B b = new B(a);
Console.WriteLine(b.Y); // SomeBitmap
a.X = AnotherBitmap;
Console.WriteLine(b.Y); // AnotherBitmap
}
中基本上有3种类型的引用。净
- 对象引用(即
A
类型的本地/param/字段,其中A
是引用类型-class
、interface
、delegate
等( - 非托管指针(即
Foo*
类型的本地/param/字段( - 托管指针(即
ref Foo
类型的本地/param(
你(相当明智地(说你不想使用";2〃;,以及";3〃;可以在堆栈上仅使用(作为局部变量或参数(;1〃;,这是可能的。例如,通过传入A
对象实例而不是int
。请参阅@Fildor的回答,了解这方面的演练。
更好的方法是将值类型封装为引用类型。考虑一下:
object o1 = new object();
object o2 = o1;
现在你可以这样使用它:
class Obj {
public int Val;
}
void Main() {
Obj o1 = new Obj();
o1.Val = 5;
Obj o2 = o1;
o2.Val = 8;
//o1.Val is 8 now
}
int
等值类型与MyClass
等参考类型之间存在巨大差异。由于您实际上对BitMap
感兴趣,是引用类型,因此不需要执行任何指针逻辑。变量已为引用。更改其状态(例如通过修改其任何公共属性(无论如何都会反映在所有其他引用中。
因此,当类A
具有类型为Bitmap
的变量时,您也可以使用任何其他代码中的变量(假设您可以访问该成员,例如,当它是private
时,您肯定不能(。
A a = new A();
B b = new B(a.Image); // this will just reference the bitmap instead of copying it
b.Image.Modify(); // this will also be reflected in a.x
我对C#不太熟悉,所以我不能提供一个有效的答案,但C#中肯定存在指针。
int* ptr = &a
查看文档:https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/pointer-types
C#区分值类型和引用类型。值类型(结构和基元类型,string
1除外(是按值传递的(复制(,而引用类型(类(本质上是花哨的指针。(顺便说一句,在IDE中,如果你把鼠标悬停在一个变量上,它会告诉你它是一个类还是一个结构(。
所以,记住这一点,这就是你的代码所做的:
class A // reference type
{
public Int32 X = 10; // with a value type field
}
class B // reference type
{
// The ctor takes in a value type parameter; a copy of the
// value is passed in (pretty much like in C++)
public B(Int32 x)
{
Y = x; // this now contains its own copy
}
// value type PROPERTY -- see below
public Int32 Y { get; set; }
}
所以,在你的主要方法中,当你进行时
A a = new A();
B b = new B(a.X);
b。Y与a.X没有连接,因为一旦对a.X求值,构造函数就会获得a.X的副本。
你能做的最简单的事情是让B的构造函数接受a类型的参数,然后在内部存储对a的引用:
class B
{
private A _a;
public B(A a) // a is passed in by reference
{
_a = a;
}
public Int32 Y {
get => _a.X;
set => _a.X = value;
}
}
现在当你做
A a = new A();
B b = new B(a);
CCD_ 18指代CCD_。更改a.X
会影响调用b.Y
的结果。
您也可以直接暴露a
:
class B
{
public B(A a)
{
A = a;
}
public A A { get; set; }
}
然后在main((中:
A a = new A();
B b = new B(a);
Console.WriteLine(b.A.X); // 10
a.X = 11;
Console.WriteLine(b.A.X); // 11
关于属性的说明
您应该注意字段和属性之间的区别。字段是数据成员,就像C++中一样。属性只是编译器在后台生成的get/set方法对的语法糖。它大致相当于这样的东西:
class B
{
private A _a;
public B(A a)
{
_a = a;
}
public A GetA() {
return _a;
}
public void SetA(A value) {
_a = value;
}
}
在这种情况下,由于A是一个引用类型,它是通过getter的引用返回的。但是现在假设A被声明为一个结构(一个值类型(。然后getter将返回一个副本。这有时会导致一个对C#初学者来说并不明显的问题。
struct A // value type
{
public Int32 X; // with a value type field
public A(Int32 x) { X = x; }
}
如果A是一个结构,并且您尝试进行
b.A.X = 11;
你会得到一个编译器错误:;无法修改"b"的返回值。A.X’,因为它不是一个变量";。
原因是b.A
返回一个副本,所以如果您更改了它上的X
,那么更改将在该行之后丢失。您必须替换整个A
实例:
A a = new A(10); // struct
B b = new B(a); // class containing a struct
Console.WriteLine(b.A.X); // 10
a.X = 11;
Console.WriteLine(b.A.X); // 10 - because A is a value type now
b.A = new A(11); // setting a property on B works as expected,
// but you can't do b.A.X = 11
Console.WriteLine(b.A.X); // 11
第页。S.在C#中,通常会编写int
而不是Int32
。
1string
是一个引用类型,但它是不可变的(所有"修改"字符串的操作都返回单独的string
实例(。