简体   繁体   中英

Pointer alternative in C#

I need an alternative for a C/C++-like pointer in C#, so that I can store the reference of a variable passed in a constructor. I want my local pointer to change its value every time the references value changes. Just like a pointer. But I don't want to use the real pointers in C# because they are unsafe. Is there a workaround?

    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
    }

Forget Pointers and start thinking in C# while coding in C# :D

I'd do something like this:

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
}

Example with Bitmap: (For simplicity, assume "SomeBitmap" and "AnotherBitmap" to be actual Bitmaps)

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
}

There are essentially 3 types of references in .NET

  1. object references (ie a local/param/field of type A , where A is a reference-type - a class , interface , delegate , etc)
  2. unmanaged pointers (ie a local/param/field of type Foo* )
  3. managed pointers (ie a local/param of type ref Foo )

You (quite wisely) say you don't want to use "2", and "3" can only be used on the stack (as local variables or parameters), so that leaves "1", which is possible. For example, by passing in an A object instance instead of an int . See @Fildor's answer for a walkthrough of this.

A better approach is to wrap your value type into a reference type. Consider this:

object o1 = new object();
object o2 = o1;

Now you can use it like this:

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
}

there´sa huge difference between value-tpes such as int and reference-typpes such as MyClass . As you´re actually interested in BitMap which is a referencetype, you don´t need to do any pointer-logic. The variable already is a reference. Changing its state eg by modifying any of its public properties will be reflected in all other references anyway.

So when your class A has a variable of type Bitmap you can use that one from any other code as well (assuming you can access that member, eg when it is private you surely cannot).

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

I'm not that familiar with C#, so I can't provide a working answer but pointers definitely exist in C#.

int* ptr = &a

Take a look at the docs: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/pointer-types

C# makes a distinction between value types and reference types . Value types (structs and primitive types, except string 1 ) are passed around by value (a copy is made), while reference types (classes) are essentially fancy pointers. (BTW, in an IDE, if you hover your mouse over a variable, it will tell you if it's a class or a struct).

So, keeping that in mind, this is what your code does:

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

So, in your main method, when you do

A a = new A();
B b = new B(a.X);

bY has no connection to aX because the constructor gets a copy of aX as soon as aX is evaluated.

The simplest thing you can do is to have the constructor of B accept a parameter of type A instead, and then store a reference to A internally:

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

Now when you do

A a = new A();
B b = new B(a);

the a refers to the same object both inside and outside of b . Changing aX will affect the result of invoking bY .

You can also expose a directly:

class B
{
    public B(A a)
    {
        A = a;
    }

    public A A { get; set; }
}

Then in 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

A note about properties

You should be aware of the difference between fields and properties. Fields are data members, just like in C++. A property is just some syntactic sugar for a get/set method pair that's generated behind the scenes by the compiler. It's roughly equivalent to something like this:

class B
{
    private A _a;
    
    public B(A a)
    {
        _a = a;       
    }

    public A GetA() { 
        return _a; 
    }     

    public void SetA(A value) { 
        _a = value;
    }      
}

In this case, since A is a reference type, it's returned by reference from the getter. But suppose now that A was declared to be a struct (a value type). Then the getter would return a copy. This sometimes causes a problem that's not obvious to C# beginners.

struct A       // value type
{
    public Int32 X;     // with a value type field
    public A(Int32 x) { X = x; }
}

If A is a struct, and you try to do

bAX = 11;

you'll get a compiler error: "Cannot modify the return value of 'bAX' because it is not a variable".

The reason is because bA returns a copy, so if you changed X on it, the change would be lost after that line. You have to replace the entire A instance instead:

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

PS In C#, you would typically write int instead of Int32 .


1 string is a reference type, however, it's immutable (all operations that "modify" strings return separate string instances).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM