C# - 在属性构造函数中抛出异常

13

我找到了关于这个主题的这篇文章,并尝试了以下方法:

public class FailerAttr : Attribute {
    public FailerAttr(string s) {
        throw new Exception("I should definitely fail!");
    }
}

在单元测试项目中,我有以下内容:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class Test {
    [TestMethod]
    public void GoFail() {
        // Make sure attribute will get initialized
        new Failer();
    }

    private class Failer {
        [FailerAttr("")]
        public int Prop { get; set; }
    }
}

当我运行测试时,它成功了。所以问题是:

  1. 为什么它没有失败?
  2. 从属性中抛出异常真的是一个不好的想法吗?因为我认为我需要这样做。

一些环境信息(以防相关):

  • 通过 ReSharper 的单元测试运行程序(R# v8.2.0.2160)运行单元测试
  • Visual Studio v11.0.61030.0

你能否使用反射获取属性? - Karel Frajták
@KarelFrajtak 我不确定,我还没有尝试过。一会儿会试试看。 - Dethariel
3
你的注释“确保属性被初始化”是错误的。只有当属性被检查时(通常通过反射),它才会被构造。 - Bun
@Bun 是的,你说得对。我想我没有很好地理解属性的概念。 - Dethariel
2个回答

16

由于属性是类定义的一部分,在运行时可用(在计算机术语中也称为“元数据”),除非程序的某个部分请求它们,否则CLR不会实例化它们。这是有道理的:为什么要浪费CPU周期去访问没有人想要访问的东西呢?

正因为如此,构造函数的执行只有在您请求该属性时才会发生。

以下是一种请求会导致程序失败的属性的方法:

var attr = Attribute.GetCustomAttribute(typeof(Failer).GetProperty("Prop"), typeof(FailerAttr));

这段代码会让CLR实例化FailerAttr,从而触发异常。

在ideone上查看演示。

如果您不知道属性的类型,可以使用此调用一次性检索所有属性:

var allAttributes = Attribute.GetCustomAttributes(typeof(Failer).GetProperty("Prop"));

这也会导致异常 (演示)。


将此标记为代码示例中如何实现失败的答案。非常感谢! - Dethariel
虽然不是很相关,但如果你的目标是.NET 4.5或更高版本,那么有泛型扩展方法(需要using System.Reflection;),因此你可以使用typeof(Failer).GetProperty("Prop").GetCustomAttribute<FailerAttr>()(单个或空)或typeof(Failer).GetProperty("Prop").GetCustomAttributes<FailerAttr>()(所有出现,可能为空)。 - Jeppe Stig Nielsen
@JeppeStigNielsen 我们正在升级到4.5.1,而我在4.0上使用自己的扩展方法来实现这个目的。感谢您的建议,我之前并不知道。 - Dethariel

7

属性不会被转换为可执行代码,它们被转换为元数据。

这样的元数据在正常执行过程中不会被使用,只有当你开始使用元数据时,比如通过反射,属性类型才会重新发挥作用。

构造函数或属性中的任何代码都不会在编译期间执行。相反,类型和构造函数的参数被序列化到元数据中,只有在使用反射进行检查时,构造函数才会真正地被执行。

换句话说,如果你想让它在编译时失败,那就不可能。

尝试使用反射查找属性,根据从元数据反序列化的属性对象,构造函数可能会被调用,也可能不会被调用,但是仅仅将其应用于标识符并不会触发构造函数。


当通过反射获取属性,例如使用 typeof(Failer).GetProperty("Prop").GetCustomAttributes() 或类似方法时,构造函数代码是否会运行或不运行(或者取决于什么)? - Jeppe Stig Nielsen
它会在你使用反射的那一部分时执行,但不会在编译时执行。 - Lasse V. Karlsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接