我可以通过引用传递对象吗

本文关键字:对象 可以通过 引用 | 更新日期: 2023-09-27 18:19:34

我正试图将一个对象从xna游戏传递到我正在编写的xna游戏库中的方法中。对象基本上可以是任何类型,包括引用类型和值类型。

游戏库用于调试目的,应该将传入对象的当前值打印到屏幕上。它目前通过调用对象上的ToString来实现这一点,但将来字符串将根据底层类型进行格式化。

以下是接收参数的游戏库方法

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

这里有一个在主游戏项目中使用它的例子

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

_counter是一个int,_message是一个字符串。

问题是,这些值的变化不会反映在屏幕上,因为(我认为)它们是通过值传递的,而不是通过引用传递的。

我尝试添加ref关键字以确保它们通过引用传递,但这会在调用The ref argument type does not match parameter type 的方法中导致错误

有没有一种方法可以通过引用传递值类型,而不必指定实际类型(如果可以避免的话,我不想为所有值类型创建方法重载)。

也欢迎任何其他建议。感谢

更新

在尝试了几种不同的方法后,我最终决定采用一种简单的方法,将Func<T>传递到AddToDebug方法中,如下所示。我意识到,至少目前,我不需要底层值,只需要它的格式化字符串表示

将来我可能会扩展库以自动扫描传入类型并显示其所有成员的值。如果涉及到这一点,我可能会使用@Hans Passant的建议来传递对有问题的班级的引用。

也就是说,我将接受@Lee的回答,因为它在我的最终实现中最有用,建议使用Func

这是我现在拥有的代码,欢迎任何改进建议。谢谢大家的帮助。

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

我可以通过引用传递对象吗

您的int不存在于堆中,因此无法创建对它的永久引用。ref引用只存在于被调用函数返回之前(以保护内存安全)。您想要的是创建对存储int位置的引用。最好的选择可能是创建一个包装类并在堆上实例化它:

class Box<T> { public T Value; }

您可以传递对该类实例的引用,并且每个拥有该引用的人都可以读取和写入该值。

_counter是一个int,_message是一个字符串。

哪些.NET类型会破坏您的计划。int是一种值类型,当您将其传递给接受对象类型参数的方法时,int将被装箱。您将获得该值的副本。无论你做什么,更新副本都不会对原始值产生影响。

类似于字符串,它是一个不可变的引用类型。它不能以任何方式更改,只能从原始字符串对象创建字符串对象。一个新的字符串,当然原始代码对此一无所知。

要使更新在其他代码中可见,需要指针。指针是相当棘手的,C#很好地支持它们,但创建一个指向变量的指针有很多陷阱。例如,您永远不能创建指向局部变量的指针,并期望该指针在方法退出时仍然有效。局部变量已不存在。无论如何,通过指针更新值都会损坏内存。使用指针时需要使用unsafe关键字的一个重要原因。

指针已经被巧妙地封装在C#中,它们被称为引用。和在创建引用类型的对象时自动使用。因此,您应该做的不仅仅是传递简单的值或字符串,而是传递对对象的引用。类对象。现在,调试代码和原始代码都使用了对完全相同对象的引用。对其中一个对象的字段和属性所做的更新对另一个对象可见。

听起来你想要一种包装变量的方法,而不是获取其当前值。

您可以创建一个包装器,每次调用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();
    }
}

然后在你的课堂上:

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