How can I keep a member var that refrences a public var in another class?

Hey there, I was trained in C++ primarily for game dev but for the last two years have been using unity and C#.

I’ve pretty much had to teach myself the differences and what not and now I’m trying to do something that I can’t seem to figure out the C# “way” to do it…

Here is some quick pseudo-code of what I’m trying to accomplish… Thanks!

public class MyClass
    {
        
        public bool bBool;

        private float* pData; 
        
        void Start()
        {
          if (bBool)
	       pData = OtherClass.fData;
          else
              pData = DifferntClass.fData;

        }
	
        public void OtherFunction(float _fNewData)
        {
            (*pData) = _fNewData;
        }
    }

Can’t do it with floats. The whole C#/Java idea specifically forbids keeping a pointer to a primitive data type. You can only have (implicit) pointers to “managed” data types.

New’d classes are “managed” by the framework, meaning there’s an invisible smart pointer which tracks them during garbage collection steps, and, ummm, makes sure they are “safe.” So you have to wrap pointed-to vars in a class. Once you have that, you can no problem say something like F.pData=12;

Of course, since all Unity monobehavior scripts are classes, in practice you keep a pointer to the script, and scriptRef-dot-varName.

— more —

Part of the trick is that all C# variables of type class are really pointers. C# encourages you to think of them as just regular vars – you NEW them yourself and always use var-dot-field. But for any class var, you always have the option to just write A=B, and you’re really reassigning the pointer.

In your mind, when you declare someClass A;, you decide whether you intend to use it as a regular variable, or as a pointer.

Standard wrapper code might be like:

class playerScript : Monobehavior { // regular Unity script

  // public float cow;
  // I want anyone to keep a pointer to this, so rewrite as float in class

  // Cow wrapper, could have been defined anywhere,
  public class floatHolder {
    public int val;
  }

  public floatHolder myCow, myDog;

  // will point to someone else's cow,dog, so I won't New it:
  floatHolder otherCow;

  void Awake() {
    // cow=6; // what I wanted to do
    // but cow is now wrapped in a class, so:
    myCow = new Cow();
    myCow.val = 6;

    // same with my dog
  }

void Start() {
  // runs after Awake, so I know Steve's cow exists:
  othercow = GameObject.Find("steve").GetComponent<playerScript>().myCow;
  otherCow.val++; // added 1 using ref to Steve's cow

  // now reassign pointer to his dog, for no reason:
  otherCow = GameObject.Find("steve").GetComponent<playerScript>().myDog;

  // can even point to one of my own:
  otherCow = myDog;
}

This solution let’s you create something similar to what you want, while not using real pointers.

First, create a new C# script file called Pointer.cs and put this inside:

using System;

public static class Pointer
{
	public static Pointer<T> Create<T>(Func<T> read, Action<T> assign)
	{
		return new Pointer<T>(read, assign);
	}
}

public class Pointer<T>
{	
	public T Value
	{
		get { return getter(); }
		set { setter(value); }
	}
	
	protected Func<T> getter;
	protected Action<T> setter;
	
	public Pointer(Func<T> getter, Action<T> setter)
	{
		this.getter = getter;
		this.setter = setter;		
	}	
	
	public static implicit operator T(Pointer<T> pointer)
	{
		return pointer.Value;
	}	
}

Now you can use it anywhere in a simple way. I will rewrite your sample code:

public class MyClass
{

    public bool bBool;

    private Pointer<float> pData; 

    void Start()
    {
      if (bBool)
       pData = Pointer.Create(()=> otherObject.fData, (value) => otherObject.fData = value);
      else
          pData = Pointer.Create(()=> differentObject.fData, (value) => differentObject.fData = value);

    }

    public void OtherFunction(float _fNewData)
    {
        pData.Value = _fNewData;
    }

    public float AnotherFunction()
    {
        return pData; // No need to specify pData.Value when returning it or assigning it to a float.
    }
}

The important part is how you create the Pointer< T >.

You call to Pointer.Create(readingLambda, assigningLambda) for that, where readingLamda is a lambda expression without parameters that returns the member from the object, and assigningLamda is a lambdaExpression with a parameter that assigns the parameter to the member of the object.

For example, let’s say I have a

class Person
{
    string name;
    int age;
}

and then in another part…

var john = new Person{name="John", age=21};
var mary = new Person{name="Mary", age=33};

var agePointer = Pointer.Create( ()=>john.age, (val) => john.age = val );
int theAge = agePointer; // theAge is now 21
agePointer.Value = 66; // john.age is now 66
agePointer = Pointer.Create( ()=>mary.age, (val)=>mary.age = val);
agePointer.Value = 15; // mary.age is now 15

This approach has the benefit (over real pointers) that the object owner of the field or property will never leave scope or be collected as long as your Pointer object itself exists. So no problems in that respect. :slight_smile: