我正在尝试将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));
答案 0 :(得分:5)
您的int
不在堆上,因此您无法创建永久引用。 ref
引用仅在被调用函数返回之前存在(以保持内存安全)。您想要的是创建对存储int
的位置的引用。您最好的选择可能是创建一个包装类并在堆上实例化它:
class Box<T> { public T Value; }
您可以将引用传递给该类的实例,并且每个拥有该引用的人都可以读取和写入该值。
答案 1 :(得分:2)
_counter是一个int,_message是一个字符串。
哪种.NET类型会破坏您的计划。 int 是一种值类型,当您将其传递给采用类型 object 的参数的方法时,int将装箱。您将获得该值的副本。无论你做什么,更新该副本都不会对原始值产生影响。
与 string 类似的故事,它是不可变的引用类型。它无法以任何方式更改,您只能从原始对象创建 new 字符串对象。原始代码当然不知道的新字符串。
让更新在其他代码中可见需要指针。指针非常棘手,C#支持它们,但创建指向变量的指针有很多熊陷阱。例如,您可以永远不会创建指向局部变量的指针,并期望指针在方法退出时仍然有效。局部变量不再存在。无论如何通过指针更新值将损坏内存。使用指针时需要使用 unsafe 关键字的一个重要原因。
指针已经巧妙地包装在C#中,它们被称为引用。并在创建引用类型的对象时自动使用。所以你应该做的不只是传递简单的值或字符串,你应该传递对象的引用。一个类对象。现在,您的调试代码和原始代码都使用对完全相同对象的引用。对象的字段和属性所做的更新在另一个中是可见的。
答案 2 :(得分:1)
听起来你想要一种方法来包装变量而不是获取它的当前值。
您可以创建一个包装器,每次调用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));