【问题标题】:Is it possible to debug a struct/class initialization member by member?是否可以按成员调试结构/类初始化成员?
【发布时间】:2020-05-27 18:15:42
【问题描述】:

像这样初始化一个类:

var x = new Item()
{
 ID = (int)...,
 Name = (string)...,
 ..
};

我在其中一项作业中收到 InvalidCastException。它们有很多,即使我逐行运行调试器,整个表达式也会出现异常。该异常并没有提供任何线索,也没有给出它试图转换为什么的任何线索。

有没有办法单独调试每个作业?我已经看到调试器在 foreach(x in y) 这样的表达式上分别停止了 3 次,所以在这里没有这样做似乎有点奇怪,并且降低了使用这种方便的初始化语法的吸引力。也许我可以使用更细粒度的调试步骤?

【问题讨论】:

  • 您可以访问Item 的源代码吗?你能在 ID、Name 的设置器上设置断点吗?
  • @JeffE 实际上是的,我猜。如果我在 evert setter 上设置断点,我至少可以看到最后一个被调用的。不理想但很好的想法,比注释掉代码或试错更好:)
  • 没有办法按照你的要求去做(至少在我使用的 VS 2017 中)。另一个想法是使用 Ctrl-K/Ctrl-C 快速注释掉对象初始化代码块。如果您以二分搜索方式执行此操作,您可以快速找到有问题的属性。
  • 如果您想缩小有问题的分配范围,为什么不尝试对每个属性或至少对其中一些属性进行逐行分配。 ------- var x = new Item(); x.ID = (int)...; x.Name = (string)...; ... ----- 如果我误解了您的问题,我深表歉意。
  • 抱歉,我没听懂您的意思...如果您在谈论错误处理,请参考:Exception Handling (C# Programming Guide) 和此:Best practices for exceptions

标签: c# visual-studio-2017 vscode-debugger


【解决方案1】:

您的问题是“是否可以按成员调试结构/类初始化成员?”。

所以,在前面,我没有按措辞直接回答 那个 问题,因为当我仔细阅读您帖子的正文时,听起来基本问题是如何识别“吸烟线”发生此 InvalidCastException 的根本原因。

我在类似情况下发现,如果 Visual Studio 可以在 InvalidCastException 发生时中断,即在特定行,那么调用堆栈和局部变量会更直接并且有用。

不幸的是,许多异常类型的 Visual Studio 默认设置抑制了“抛出时中断”。但是很容易为所有异常打开“抛出时中断”。只需在 Visual Studio 的“例外”窗口中更改此默认设置即可:

到这里:

这并不“总是”有帮助,但它是一个好的开始。这很容易,为什么不先尝试一下,看看是否可以快速解决。希望这对您的情况有用。

【讨论】:

    【解决方案2】:

    不确定这是否是 VS 2017 中的一个选项,我手头只有 2019 年。在选项 -> 调试 -> 常规下的设置中,取消选中跳过属性和操作符。然后在初始化程序处设置一个断点并使用 F11(Step-Into)单步执行。您将点击每个属性设置器,直到抛出异常。

    【讨论】:

    • 这是一个非常好的观点,是的,我可以肯定地告诉你,只要我记得,这个设置就已经可用(比如,VS 2005)。是的,如果没有这个,整个“调试属性”的事情就无法启动。
    【解决方案3】:

    如果我遗漏了某些东西(如果是这种情况,将删除)但使用了这样的无效演员表,请原谅:

    struct Item
    {
        public int ID { get; set; }
        public Derived Derived { get; set; }
    }
    public class Base
    {
        public string Name { get; set; }
    }
    public class Derived : Base
    {
        public string AdditionalProperty { get; set; }
    }
    
    var baseClass = new Base()
    {
        Name = "foo",
    };
    try
    {
        var x = new Item()
        {
              ID = (int)20,
              Derived = (Derived)baseClass,
        };
    }
    catch(Exception e)
    {
              Console.WriteLine(e.Message);
    }
    

    巧妙地捕捉到这样的错误 Unable to cast object of type 'Base' to type 'Derived'. 在 vs2017 中

    但是这会在第一个错误时中断,我们可能希望以这种方式初始化一个对象并记录所有转换错误而不退出初始化。我们可以通过为此示例实现自己的转换来做到这一点:

    public static T TryCast<T>(Object _object)
            {
                try
                {
                    return (T)_object;
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Cant cast object of type {_object.GetType().ToString()} to object of type {typeof(T)}");
                }
                return default(T);
            }
     public static T TryCast<T>(IConvertible _object)
            {
                try
                {
                    return (T)Convert.ChangeType(_object, typeof(T));
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Cant convert object of type {_object.ToString()} to object of type {typeof(T)}");
                }
                return default(T);
            }
    

    用于演示目的的新类型

    struct Item
        {
            public int ID { get; set; }
            public double FooDouble { get; set; }
            public Base Base { get; set; }
            public Derived Derived { get; set; }
            public string Bar { get; set; }
        }
        public class Base
        {
            public string Name { get; set; }
        }
        public class Derived : Base
        {
            public string AdditionalProperty { get; set; }
        }
    

    然后我们可以像这样初始化我们的对象:

    var derived = new Derived()
                {
                    Name = "DerivedFoo",
                    AdditionalProperty = "Bar"
                };
                var _base = new Base()
                {
                    Name = "BaseFoo"
                };
                var x = new Item()
                {
                    ID = Utils.TryCast<int>("please no"),
                    FooDouble = Utils.TryCast<double>(2),
                    Base = Utils.TryCast<Base>(derived),
                    Derived = Utils.TryCast<Derived>(_base),
                    Bar = "Foo"
                };
    

    而且我们整齐地记录了投射时可能发生的任何错误:

    【讨论】:

    • 您的解决方案的问题在于,它常常无济于事,因为它忽略了转换,例如T=int 的 (T) 与 (int) 有很大不同。前者仅在传递的对象确实是 int 时才有效,而后者也适用于 uint。
    • 你是对的,为 TryCast 实现另一个重载会解决这个问题吗?见编辑
    • 是的,我认为至少如果编译器知道实际类型是什么,重载将解决这个问题。然后,唯一剩下的问题是通过 IConvertible 的解决方案带有 Boxing,这会导致一些开销。这个开销是否可以接受可能取决于场景。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多