简体   繁体   English

我可以通过引用传递对象

[英]Can I pass an object by reference

I am trying to pass an object from an xna game into a method in an xna game library I'm writing. 我试图将一个xna游戏中的对象传递给我正在编写的xna游戏库中的方法。 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. 游戏库用于调试目的,应该向屏幕打印传入的对象的当前值。它目前通过调用对象上的ToString来执行此操作,但将来字符串将根据基础类型进行格式化。

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. _counter是一个int, _message是一个字符串。

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 我尝试添加ref关键字以确保它们通过引用传递但是在方法调用中导致错误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 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. 在尝试了几种不同的方法后,我最终确定了将Func<T>传递给AddToDebug方法的AddToDebug方法,如下所示。 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. 如果是这样的话,我可能会使用@Hans Passant的建议来传递对相关课程的引用。

That said I will accept the answer from @Lee as it was most useful in my final implementation by suggesting the use of Func . 这就是说我会接受来自@Lee的答案,因为它在我的最终实现中最有用,建议使用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));

Your int does not live on the heap, so you cannot create a permanent reference to it. 您的int不在堆上,因此您无法创建对它的永久引用。 A ref reference only lives until the called function returns (to preserve memory safety). ref引用仅在被调用函数返回之前存在(以保持内存安全性)。 What you want is to create a reference to the location the int is stored. 你想要的是创建一个int存储位置的引用。 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. _counter是一个int,_message是一个字符串。

Which are .NET types that will defeat your plan. 哪种.NET类型会破坏您的计划。 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 . int是一个值类型,当你将它传递给一个接受类型为object的参数的方法时,int将被装箱 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. 指针非常棘手,C#支持它们,但创建指向变量的指针有很多熊陷阱。 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. 使用指针时需要使用unsafe关键字的一个重要原因。

Pointers are already ably wrapped in C#, they are called references . 指针已经巧妙地包装在C#中,它们被称为引用 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. 并且对一个对象的字段和属性所做的更新对另一个是可见的。

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: 您可以创建一个包装器,每次调用ToString都会获取当前值,然后从包含的类中传递一个委托来获取变量:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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