英文:
Null object not being null
问题
Here's the translated code:
我编写了以下函数,用于在我的Unity项目中进行健全性检查时提供便捷的工具。
namespace MyProject.Utilities
{
public class DebugUtils
{
/// <summary>
/// 检查传递的对象是否为null,如果是,则打印到控制台。
/// </summary>
/// <param name="obj">要检查的对象。</param>
public static void DebugAssertNull<T>(T obj, string tag = "M_UTILS_DEBUG")
{
if (obj == null)
{
StackTrace stackTrace = new();
StackFrame stackFrame = stackTrace.GetFrame(1);
string methodName = stackFrame.GetMethod().Name;
string fileName = stackFrame.GetFileName();
int lineNumber = stackFrame.GetFileLineNumber();
string message = $"[{tag}] 类型为 {typeof(T)} 的对象在方法 {methodName} 中为null,位于 {fileName}:{lineNumber}";
UnityEngine.Debug.Assert(false, message);
}
}
}
}
现在,当我从另一个脚本中调用这个函数时,如下所示:
public class SomeBehaviour : MonoBehaviour
{
public GameObject someObject;
void Start()
{
DebugAssertNull<GameObject>(someObject);
}
}
请注意,翻译结果中只包含代码部分,没有其他内容。
英文:
I wrote the following function to have a handy utility for sanity checks in my unity projects.
namespace MyProject.Utilities
{
public class DebugUtils
{
/// <summary>
/// Checks if passed object is null and prints to console if it is.
/// </summary>
/// <param name="obj">The obj.</param>
public static void DebugAssertNull<T>(T obj, string tag = "M_UTILS_DEBUG")
{
if (obj == null)
{
StackTrace stackTrace = new();
StackFrame stackFrame = stackTrace.GetFrame(1);
string methodName = stackFrame.GetMethod().Name;
string fileName = stackFrame.GetFileName();
int lineNumber = stackFrame.GetFileLineNumber();
string message = $"[{tag}] Object of type {typeof(T)} is null in method {methodName} at {fileName}:{lineNumber}";
UnityEngine.Debug.Assert(false, message);
}
}
}
}
Now when I call this function from another script as follows
public class SomeBehaviour: MonoBehaviour{
public GameObject someObject;
void Start(){
DebugAssertNull<GameObject>(someObject);
}
}
Now I am expecting that a message prints to the console. However upon running this code obj turns out to be not null. Using the debugger in Visual Studio 2019, setting up a watch obj == null
evaluates to true.
I feel like there is some implementation detail I am missing here. I am working with unity 2021, Visual Studio 2019, and .NET Framework 4.
答案1
得分: 5
以下是翻译好的内容:
除了这个答案之外,还有以下内容。
由于您的方法使用了没有任何类型限制的泛型,它将使用最通用的 ==
实现,即适用于 System.Object
。
如果您明确限制类型,它将按预期工作,例如:
public static void DebugAssertNull<T>(T obj, string tag = "M_UTILS_DEBUG") where T : UnityEngine.Object
{
if (obj == null)
{
StackTrace stackTrace = new();
StackFrame stackFrame = stackTrace.GetFrame(1);
string methodName = stackFrame.GetMethod().Name;
string fileName = stackFrame.GetFileName();
int lineNumber = stackFrame.GetFileLineNumber();
string message = $"[{tag}] Object of type {typeof(T)} is null in method {methodName} at {fileName}:{lineNumber}";
UnityEngine.Debug.Assert(false, message);
}
}
这种方式肯定会使用重载的 UnityEngine.Object ==
。
如果这意味着要用于 Unity Inspector 中引用的内容,那么这就足够了。
在 Inspector 中唯一按引用传递的东西就是 UnityEngine.Object
。其他一切都作为值直接反序列化,永远不会为 null
。
然后问题可能是,为什么要为这种情况使用这样的调试工具,而不是直接检查,如下所示:
private void Start()
{
if (!someObject) Debug.LogError($"{nameof(someObject)} is not assigned", this);
}
这样做的好处是:
- 双击日志消息,直接跳转到相应的代码行
- 单击消息后,突出显示层次结构中的相应对象(或资产)
英文:
Just in addition to this answer.
Since your method uses generics without any type limitation it will use the most generic ==
implementation which is for System.Object
.
It works as expected if you explicitly limit the type like e.g.
public static void DebugAssertNull<T>(T obj, string tag = "M_UTILS_DEBUG") where T : UnityEngine.Object
{
if (obj == null)
{
StackTrace stackTrace = new();
StackFrame stackFrame = stackTrace.GetFrame(1);
string methodName = stackFrame.GetMethod().Name;
string fileName = stackFrame.GetFileName();
int lineNumber = stackFrame.GetFileLineNumber();
string message = $"[{tag}] Object of type {typeof(T)} is null in method {methodName} at {fileName}:{lineNumber}";
UnityEngine.Debug.Assert(false, message);
}
}
This way it definitely uses the overload UnityEngine.Object ==
.
If this is intended to be used for stuff referenced in the Unity Inspector it should also be enough.
The only thing going by reference in the Inspector is UnityEngine.Object
. Everything else is deserialized as values directly and never null
.
Question would then be though why have such a debug tool for this case instead of simply checking directly
private void Start()
{
if(!someObject) Debug.LogError($"{nameof(someObject)} is not assigned", this);
}
advantage of this would be:
- Double click on the log message and directly jump to according code line
- Click once on the message highlights according object in the Hierarchy (or the assets)
答案2
得分: 1
这是一个古老的Unity... 嗯... 让我们称之为一个“特性” =)
原因是真正的Unity对象在C++部分,而C#对象只是这些对象的包装器。这就是为什么基本的Unity3d对象重写了 ==
运算符,并在与空值比较时返回 true
,即使对象仍然存在。
你可以在这里找到更多信息。
英文:
It's an old Unity... hmm... let's call it a "feature" =)
The reason is that real Unity objects are in the C++ part and C# objects are only wrappers for these objects. That's why the basic Unity3d object overrides ==
operator and returns true
in case of comparing with null even object still exists.
You can find more information here
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论