4

在我的自定义属性的静态构造函数中,我在加载的程序集中搜索所有用我的属性修饰的类,并对它们执行一些操作。

我希望在运行时尽快调用静态构造函数,最好是在执行static void Main()入口点之前。

目前它只有在我对属性进行一些调用后才会被调用。我可以在程序的其他地方进行这样的调用,但理想情况下,属性的功能是独立的。

寻找答案,我在MSDN上读到了这个:

用户无法控制何时在程序中执行静态构造函数。

但肯定有一些棘手的、狡猾的或恶作剧的解决方法来尽快调用静态构造函数。也许可以使用属性、反射或其他某种魔法。可以做到吗?

因为人们无疑会告诉我没有充分的理由去做我所要求的,所以我提出我的目的和我的代码:我正在尝试使用属性来以声明方式配置一个db4o工厂。如果我的属性的静态构造函数在我已经建立连接之后被调用,那么它没有任何作用并且没有用。因此,必须在我的程序有机会建立这样的连接之前调用它。

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
sealed public class CascadeOnUpdateAttribute : Attribute
{
    public bool Flag { get; private set; }

    public CascadeOnUpdateAttribute() : this(true) { }

    public CascadeOnUpdateAttribute(bool flag)
    {
        Flag = flag;
    }

    static CascadeOnUpdateAttribute()
    {
        var targets = from assembly in AppDomain.CurrentDomain.GetAssemblies()
          from type in assembly.GetTypes()
          from attribute in type.GetCustomAttributes(typeof(CascadeOnUpdateAttribute), false).Cast<CascadeOnUpdateAttribute>()
          select new { Type = type, Cascade = attribute.Flag };

        foreach (var target in targets)
        {
            Db4oFactory.Configure().ObjectClass(target.Type).CascadeOnUpdate(target.Cascade);
        }
    }
}

更新:

我最终使用了带有静态方法的抽象属性。通过这种方式,我可以导出任意数量的属性,并且它们都将通过调用这个方法应用于指定的配置。

public abstract class Db4oAttribute : Attribute
{
    public abstract void Configure(IConfiguration config, Type type);

    public static void ApplyAttributes(IConfiguration config)
    {
        var targets = from assembly in AppDomain.CurrentDomain.GetAssemblies()
          from type in assembly.GetTypes()
          from attribute in type.GetCustomAttributes(typeof(Db4oAttribute), false).Cast<Db4oAttribute>()
          select new { Type = type, Attribute = attribute };

        foreach (var target in targets)
        {
            target.Attribute.Configure(config, target.Type);
        }
    }
}

和呼叫站点:

Db4oAttribute.ApplyAttributes(Db4oFactory.Configure());
_db = Db4oFactory.OpenFile("Test.db4o");
4

4 回答 4

3

如果您希望调用静态构造函数,则向该类型添加一个虚拟方法,并在代码开头(Main等)简单地调用它;如果它是一个琐碎/空的方法,您可能希望将其标记为不进行内联等。

class SomeType {
    static SomeType() {
        Console.WriteLine("SomeType.cctor");
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void Init() { }
}

static class Program {
    static void Main() {
        SomeType.Init();
        Console.WriteLine("hi");
    }
}

可以使用反射来调用静态构造函数,但我不推荐;如果您使用反射,您实际上可以多次调用 .cctor,这绝不是一件好事......

于 2009-12-11T07:11:00.050 回答
3

Main正如马克所说,如果我是你,我会明确地这样做。

可以Type.TypeInitializer使用属性显式调用类型的类型初始化程序并调用它。但是,即使它已经运行,这也会导致它再次运行,这可能会产生意想不到的结果。

我个人会将该代码完全移出静态初始化程序。它是配置代码——为什么不把它变成一个可以显式调用的静态方法呢?我什至不确定我是否将它放在属性类本身中,但至少明确调用:

CascadeOnUpdateAttribute.ConfigureDb4oFactories();

比调用虚拟方法或以其他方式强制类型初始化更清楚,只是为了获得副作用。

于 2009-12-11T07:15:40.663 回答
2

您可以通过调用避免静态虚拟方法

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(CascadeOnUpdateAttribute).TypeHandle)
于 2010-11-11T17:32:45.550 回答
1

我认为使用静态构造函数有异味;我会考虑重构您的代码以控制对 db4o 工厂的访问,这样您就不需要使用它。

于 2009-12-11T07:16:34.050 回答