Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/url/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Exception.Message与Exception.ToString()的比较_C#_.net_Exception_Exception Handling - Fatal编程技术网

C# Exception.Message与Exception.ToString()的比较

C# Exception.Message与Exception.ToString()的比较,c#,.net,exception,exception-handling,C#,.net,Exception,Exception Handling,我有记录异常的代码。消息。然而,我读了一篇文章,其中指出最好使用Exception.ToString()。使用后者,您可以保留有关错误的更重要信息 这是真的吗?是否可以继续并替换所有代码记录异常。消息 我还使用了一个基于XML的布局。Exception.ToString()是否可能包含可能导致问题的无效XML字符?仅包含与异常相关的消息(doh)。例如: 对象引用未设置为对象的实例 该方法将提供更详细的输出,包含异常类型、消息(来自之前)、堆栈跟踪,以及嵌套/内部异常的所有这些内容。更准确地说,

我有记录异常的代码。消息。然而,我读了一篇文章,其中指出最好使用
Exception.ToString()
。使用后者,您可以保留有关错误的更重要信息

这是真的吗?是否可以继续并替换所有代码记录
异常。消息

我还使用了一个基于XML的布局。
Exception.ToString()
是否可能包含可能导致问题的无效XML字符?

仅包含与异常相关的消息(doh)。例如:

对象引用未设置为对象的实例

该方法将提供更详细的输出,包含异常类型、消息(来自之前)、堆栈跟踪,以及嵌套/内部异常的所有这些内容。更准确地说,该方法返回以下内容:

ToString返回当前异常的表示形式,以供人类理解。如果异常包含区域性敏感数据,则需要ToString返回的字符串表示形式考虑当前系统区域性。虽然对返回字符串的格式没有确切的要求,但它应该尝试反映用户所感知的对象的值

ToString的默认实现获取引发当前异常的类的名称、消息、对内部异常调用ToString的结果以及调用Environment.StackTrace的结果。如果这些成员中的任何一个是空引用(Visual Basic中没有),则其值不包含在返回的字符串中

如果没有错误消息或是空字符串(“”),则不会返回错误消息。仅当内部异常和堆栈跟踪不是空引用时(在Visual Basic中为Nothing),才会返回它们的名称


嗯,我想说这取决于你想在日志中看到什么,不是吗?如果您对ex.Message提供的内容感到满意,请使用它。否则,请使用ex.toString()甚至记录堆栈跟踪。

取决于您需要的信息。对于调试堆栈跟踪和内部异常,以下选项非常有用:

    string message =
        "Exception type " + ex.GetType() + Environment.NewLine +
        "Exception message: " + ex.Message + Environment.NewLine +
        "Stack trace: " + ex.StackTrace + Environment.NewLine;
    if (ex.InnerException != null)
    {
        message += "---BEGIN InnerException--- " + Environment.NewLine +
                   "Exception type " + ex.InnerException.GetType() + Environment.NewLine +
                   "Exception message: " + ex.InnerException.Message + Environment.NewLine +
                   "Stack trace: " + ex.InnerException.StackTrace + Environment.NewLine +
                   "---END Inner Exception";
    }

除了已经说过的,不要在异常对象上使用
ToString()
,以便向用户显示。仅使用
消息
属性就足够了,或者使用更高级别的自定义消息


就日志记录目的而言,一定要对异常使用
ToString()
,而不仅仅是
Message
属性,因为在大多数情况下,您会对异常发生的具体位置以及调用堆栈是什么感到困惑。stacktrace会告诉你所有这些。

我会说@Wim是对的。您应该对日志文件使用
ToString()
(假设有技术观众)和
消息
(如果有的话)向用户显示。有人可能会争辩说,即使这样,对于用户来说也不合适,因为存在每种异常类型和发生情况(想想ArgumentExceptions,等等)

此外,除了StackTrace之外,
ToString()
还将包含其他方式无法获得的信息。例如,fusion的输出,如果要在异常“消息”中包含日志消息


某些异常类型甚至在
ToString()
中包含附加信息(例如来自自定义属性),但不在消息中。

就log4net的XML格式而言,您不必担心日志的ex.ToString()。只需传递exception对象本身,log4net就会以预先配置的XML格式提供所有详细信息。我偶尔遇到的唯一一件事是新行格式,但那是我在读取原始文件时遇到的。否则,解析XML非常有效。

将整个异常转换为字符串 调用提供了比仅使用属性更多的信息。然而,即使这样,仍然遗漏了很多信息,包括:

  • 在所有异常上都可以找到
    数据
    集合属性
  • 添加到异常的任何其他自定义属性
  • 有时您需要捕获这些额外信息。下面的代码处理上述场景。它还以良好的顺序写出异常的属性。它使用的是C#7,但如果需要的话,应该很容易转换到旧版本。另见相关答案

    public static class ExceptionExtensions
    {
        public static string ToDetailedString(this Exception exception) =>
            ToDetailedString(exception, ExceptionOptions.Default);
    
        public static string ToDetailedString(this Exception exception, ExceptionOptions options)
        {
            if (exception == null)
            {
                throw new ArgumentNullException(nameof(exception));
            } 
    
            var stringBuilder = new StringBuilder();
    
            AppendValue(stringBuilder, "Type", exception.GetType().FullName, options);
    
            foreach (PropertyInfo property in exception
                .GetType()
                .GetProperties()
                .OrderByDescending(x => string.Equals(x.Name, nameof(exception.Message), StringComparison.Ordinal))
                .ThenByDescending(x => string.Equals(x.Name, nameof(exception.Source), StringComparison.Ordinal))
                .ThenBy(x => string.Equals(x.Name, nameof(exception.InnerException), StringComparison.Ordinal))
                .ThenBy(x => string.Equals(x.Name, nameof(AggregateException.InnerExceptions), StringComparison.Ordinal)))
            {
                var value = property.GetValue(exception, null);
                if (value == null && options.OmitNullProperties)
                {
                    if (options.OmitNullProperties)
                    {
                        continue;
                    }
                    else
                    {
                        value = string.Empty;
                    }
                }
    
                AppendValue(stringBuilder, property.Name, value, options);
            }
    
            return stringBuilder.ToString().TrimEnd('\r', '\n');
        }
    
        private static void AppendCollection(
            StringBuilder stringBuilder,
            string propertyName,
            IEnumerable collection,
            ExceptionOptions options)
            {
                stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
    
                var innerOptions = new ExceptionOptions(options, options.CurrentIndentLevel + 1);
    
                var i = 0;
                foreach (var item in collection)
                {
                    var innerPropertyName = $"[{i}]";
    
                    if (item is Exception)
                    {
                        var innerException = (Exception)item;
                        AppendException(
                            stringBuilder,
                            innerPropertyName,
                            innerException,
                            innerOptions);
                    }
                    else
                    {
                        AppendValue(
                            stringBuilder,
                            innerPropertyName,
                            item,
                            innerOptions);
                    }
    
                    ++i;
                }
            }
    
        private static void AppendException(
            StringBuilder stringBuilder,
            string propertyName,
            Exception exception,
            ExceptionOptions options)
        {
            var innerExceptionString = ToDetailedString(
                exception, 
                new ExceptionOptions(options, options.CurrentIndentLevel + 1));
    
            stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
            stringBuilder.AppendLine(innerExceptionString);
        }
    
        private static string IndentString(string value, ExceptionOptions options)
        {
            return value.Replace(Environment.NewLine, Environment.NewLine + options.Indent);
        }
    
        private static void AppendValue(
            StringBuilder stringBuilder,
            string propertyName,
            object value,
            ExceptionOptions options)
        {
            if (value is DictionaryEntry)
            {
                DictionaryEntry dictionaryEntry = (DictionaryEntry)value;
                stringBuilder.AppendLine($"{options.Indent}{propertyName} = {dictionaryEntry.Key} : {dictionaryEntry.Value}");
            }
            else if (value is Exception)
            {
                var innerException = (Exception)value;
                AppendException(
                    stringBuilder,
                    propertyName,
                    innerException,
                    options);
            }
            else if (value is IEnumerable && !(value is string))
            {
                var collection = (IEnumerable)value;
                if (collection.GetEnumerator().MoveNext())
                {
                    AppendCollection(
                        stringBuilder,
                        propertyName,
                        collection,
                        options);
                }
            }
            else
            {
                stringBuilder.AppendLine($"{options.Indent}{propertyName} = {value}");
            }
        }
    }
    
    public struct ExceptionOptions
    {
        public static readonly ExceptionOptions Default = new ExceptionOptions()
        {
            CurrentIndentLevel = 0,
            IndentSpaces = 4,
            OmitNullProperties = true
        };
    
        internal ExceptionOptions(ExceptionOptions options, int currentIndent)
        {
            this.CurrentIndentLevel = currentIndent;
            this.IndentSpaces = options.IndentSpaces;
            this.OmitNullProperties = options.OmitNullProperties;
        }
    
        internal string Indent { get { return new string(' ', this.IndentSpaces * this.CurrentIndentLevel); } }
    
        internal int CurrentIndentLevel { get; set; }
    
        public int IndentSpaces { get; set; }
    
        public bool OmitNullProperties { get; set; }
    }
    
    顶部提示-日志记录异常 大多数人将使用此代码进行日志记录。考虑使用我的NuGET包,它也记录异常的所有属性,但是在大多数情况下做得更快,没有反射。Serilog是一个非常高级的日志框架,在撰写本文时非常流行

    顶部提示-人类可读的堆栈跟踪
    您可以使用NuGet包为异常获取人类可读的堆栈跟踪,如果您使用的是Serilog,则可以使用NuGet包。

    这或多或少是
    Exception.ToString()
    将为您提供的,对吗?@Matt:在这种情况下,构建一个
    StringBuilder
    实例可能比两个新的字符串分配更昂贵,这是一个很有争议的问题,在这里效率更高。我们不是在处理迭代。这里的问题是,您只能得到最外层异常的“内部异常”。注意,如果InnerException本身有一个InnerException集,那么您不会转储它(假设您首先要转储它)。我会坚持使用ToString()。只需使用ex.ToString即可。它会告诉你所有的细节。@Christian:编译器有多个+s是正常的。例如,请参见“+运算符易于使用,并有助于编写直观的代码。即使在一条语句中使用多个+运算符,字符串内容也只复制一次。”from ex.ToString包括堆栈跟踪+1。在日志中只看到“对象引用未设置为对象实例”是非常痛苦的。你觉得很无助。:-)F