Pregunta

I am trying to pass an object from an xna game into a method in an xna game library I'm writing. The object could be basically any type, both reference and value types.

The game library is for debug purposes and should print out to the screen the current value of the object passed in. It currently does this by calling ToString on the object but in the future the string will be formatted depending on the underlying type.

Here is the game library method that receives the argument

    public void AddToDebug(object obj)
    {
        var type = obj.GetType();
        _debugTypes.Add(obj, type);
    }

And here is an example of using it from within the main game project

VD.AddToDebug(_counter);
VD.AddToDebug(_message);

_counter is an int and _message is a string.

The issue is that changes in these values are not reflected on screen because (I assume) they are passed by value and not by reference.

I tried adding the ref keyword to ensure they are passed by reference but that causes an error in the method calls The ref argument type does not match parameter type

Is there a way I can pass value types by reference without having to specify the actual type (I don't want to have to create method overloads for all value types if I can avoid it).

Any other suggestions also welcomed. Thanks

UPDATE

After trying several different ways I eventually settled on the simple approach of passing a Func<T> into the AddToDebug method as shown below. I realised that, at least at the moment, I don't need the underlying value but only a formatted string representation of it.

In the future I may expand the library to automatically scan a passed in type and display values of all its members. If it comes to that I will likely use the suggestion from @Hans Passant to pass in a reference to the Class in question.

That said I will accept the answer from @Lee as it was most useful in my final implementation by suggesting the use of Func.

This is the code I have now and would welcome any suggestions for improvement. Thank you all for your help.

    public void AddToDebug<T>(Func<T> getValue, Color? color = null)
    {
        var w = new DebugEntry<T>(getValue, color.HasValue ? color.Value : Color.White);
        _values.Add(w);
    }

    public class DebugEntry<T> : IStringFormatter
    {
        private readonly Func<T> _getValue;
        public Color Color { get; private set; }

        public DebugEntry(Func<T> getValue, Color color)
        {
            _getValue = getValue;
            Color = color;
        }

        public string ToFormattedString()
        {
            return _getValue.Invoke().ToString();
        }
    }

    public interface IStringFormatter
    {
        string ToFormattedString();
        Color Color { get; }
    }

    // Example usage
    VD.AddToDebug(() => _counter);
    VD.AddToDebug(() => String.Format("This message says {0} times", _message));
¿Fue útil?

Solución 3

It sounds like you want a way to wrap a variable instead of getting its current value.

You could create a wrapper which fetches the current value each time ToString is called, then pass a delegate from the containing class to get the variable:

public class Wrapper<T>
{
    private readonly Func<T> getter;
    public Wrapper(Func<T> getter)
    {
        this.getter = getter;
    }

    public override string ToString()
    {
        return getter().ToString();
    }
}

then in your class:

AddToDebug(new Wrapper<int>(() => this.intField));

Otros consejos

Your int does not live on the heap, so you cannot create a permanent reference to it. A ref reference only lives until the called function returns (to preserve memory safety). What you want is to create a reference to the location the int is stored. Your best option is probably to create a wrapper class and instantiate it on the heap:

class Box<T> { public T Value; }

You can pass a reference to an instance of that class and everyone who has the reference can read and write the value.

_counter is an int and _message is a string.

Which are .NET types that will defeat your plan. An int is a value type, when you pass it to a method that takes an argument of type object then the int will be boxed. You'll get a copy of the value. Updating that copy will never have an effect on the original value, no matter what you do.

Similar story with string, it is an immutable reference type. It cannot be changed in any way, you can only create a new string object from the original. A new string that the original code knows nothing about of course.

Getting updates to be visible in other code requires pointers. Pointers are pretty tricky, C# supports them well enough but creating a pointer to a variable has a lot of bear traps. You can for example never create a pointer to a local variable and expect that pointer to still work when the method has exited. The local variable doesn't exist anymore. Updating the value through the pointer anyway will corrupt memory. A big reason you need to use the unsafe keyword when working with pointers.

Pointers are already ably wrapped in C#, they are called references. And are automatically used when you create an object of a reference type. So what you should do is not just pass simple value or strings, you should pass a reference to an object instead. A class object. Now both your debugging code and your original code are using a reference to the exact same object. And updates made to fields and properties of the object in one are visible to the other.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top