如何触发静态构造函数

发布于 2024-11-27 13:32:50 字数 1599 浏览 2 评论 0原文

代码:

class Base<T,U> where T:Base<T,U>,new()  where U :class
{
    protected static U _val = null;
    internal static void ShowValue()
    {
        if(_val == null)new T(); //Without this line, it won't work as expected
        Console.WriteLine (_val);
    }
    internal static void Virtual()
    {
        Console.WriteLine ("Base");
    }
}
class Deriv :Base<Deriv,string>
{
    static Deriv()
    {
        _val = "some string value";
    }
    internal static new void Virtual ()
    {
        Console.WriteLine ("Deriv");
    }
}
 public static void Main (string[] args)
{
    Deriv.ShowValue();            
    Deriv.Virtual();
}

感谢 .NET 的泛型,我可以使用泛型基类中定义的泛型静态方法创建一堆特定的类。它可以在一定程度上模仿继承多态性。但为了初始化不同版本的静态字段,我必须使用静态构造函数。不幸的是,我们不能直接调用它们,因此,我们必须找到一种方法来触发它的调用。上面给出的例子展示了一种方法。但我不喜欢实例化或反射方法。我们也无法对泛型参数的静态方法进行约束。所以我想问一下,是否还有其他方法可以完成这种工作!

预先感谢!

~~~~~~~~~~~~~~~~

一些结论(也许有点早):

似乎没有解决方法来处理这种情况。我必须实例化一个子类或使用反射。考虑到 .cctors 只需要调用一次,我赞成反射方法,因为在某些情况下, new() 约束不是一个选择 - 就像您不应该向用户公开无参数 ctor 一样。

经过进一步的实验,我发现.cctors可能被多次调用,但只有第一次调用才会影响静态字段的设置。这很奇怪,但却是一个很好的奇怪!

    class MyClass 
    {
        static int _val = 0;
        static MyClass()
        {
            _val++;
            Console.WriteLine (_val);
        }
    }
    public static void Main (string[] args)
    {
        ConstructorInfo ci = typeof(MyClass).TypeInitializer;
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
    }
//result:
//1
//1
//1
//1

code:

class Base<T,U> where T:Base<T,U>,new()  where U :class
{
    protected static U _val = null;
    internal static void ShowValue()
    {
        if(_val == null)new T(); //Without this line, it won't work as expected
        Console.WriteLine (_val);
    }
    internal static void Virtual()
    {
        Console.WriteLine ("Base");
    }
}
class Deriv :Base<Deriv,string>
{
    static Deriv()
    {
        _val = "some string value";
    }
    internal static new void Virtual ()
    {
        Console.WriteLine ("Deriv");
    }
}
 public static void Main (string[] args)
{
    Deriv.ShowValue();            
    Deriv.Virtual();
}

Thanks to the generics of .NET, I can create a bunch of specific classes reusing generic static methods defined in the generic base class. It can mimic inheritance polymorphism to some extent. But in order to initialize different version of static fields, I've to use static constructors. Unfortunately, we can't call them directly, therefore, we have to figure out a way to trigger it's invocation. The example given above showed a way. But I don't like either the instantiation,or the reflection approach. We also can't make a constraint on a static method of a generic parameter. So, I'd like to ask, if there is another way to do this kind of job!

Thanks beforehand!

~~~~~~~~~~~~~~~~

Some Conclusion (Maybe a little early):

It seems there is no workaround to deal with this kind of situation. I have to instantiate a subclass or use reflection. Considering the .cctors need merely be called once, I'm in favor of the reflection approach, because in some case, a new() constraint is just not a choice - like you're not supposed to expose the parameterless ctor to user.

After conducting further experiment, I find out that the .cctors may be called multi-times but only the first invocation will affect the setting of static fields. That's weird, but a good weirdness!

    class MyClass 
    {
        static int _val = 0;
        static MyClass()
        {
            _val++;
            Console.WriteLine (_val);
        }
    }
    public static void Main (string[] args)
    {
        ConstructorInfo ci = typeof(MyClass).TypeInitializer;
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
    }
//result:
//1
//1
//1
//1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

对你的占有欲 2024-12-04 13:32:50

我强烈建议您重新考虑您的设计。尝试使用这种“静态继承”解决方法正在与 .NET 的一些核心设计作斗争。

目前还不清楚您要解决的更大问题是什么,但是尝试像这样的“聪明”代码来模拟继承将导致代码从长远来看很难维护和诊断。

如果没有实际上使用Deriv的成员(或创建它的实例),您基本上不会触发静态构造函数。重要的是要了解 Deriv.ShowValue() 基本上会转换为对 ... 的调用,

Base<Deriv, string>.ShowValue();

因此您实际上并没有在 Deriv 上调用任何内容>。如果这样编写,您的调用代码实际上会更清晰。

编辑:避免显式使用类型初始值设定项的另一个(显然不幸的)原因是 .NET 4.5 中存在一个错误,该错误会导致在某些情况下不适当地引发异常。有关详细信息,请参阅我关于该主题的问题

I would strongly advise you to rethink your design. Attempting to use this sort of workaround for "static inheritance" is fighting against some of the core designs of .NET.

It's unclear what bigger problem you're trying to solve, but trying "clever" code like this to simulate inheritance will lead to code which is very hard to maintain and diagnose in the longer term.

Without actually using a member of Deriv (or creating an instance of it), you basically won't trigger the static constructor. It's important to understand that Deriv.ShowValue() is basically converted into a call to

Base<Deriv, string>.ShowValue();

... so you're not actually calling anything on Deriv. Your calling code would actually be clearer if it were written that way.

EDIT: One other (clearly unfortunate) reason to avoid using type initializers explicitly is that there's a bug in .NET 4.5 which causes an exception to be thrown inappropriately in some cases. See my question on the topic for more information.

妄司 2024-12-04 13:32:50

正确的解决方案是像这样调用类型初始值设定项(= 静态构造函数):

typeof(T).TypeInitializer.Invoke(null, null);

它需要两个 null。仅指定一个会引发 MemberAccessException

因此,您的代码可能看起来像这样:

internal static void ShowValue()
{
    if (_val == null)
    {
        if (typeof(T).TypeInitializer != null)
            typeof(T).TypeInitializer.Invoke(null, null);
        if (_val == null)
            throw new InvalidOperationException(string.Format("The type initializer of {0} did not initialize the _val field.", typeof(T)));
    }
    Console.WriteLine(_val);
}

这样,您就可以删除 new() 约束。

The correct solution is to invoke the type initializer (= static constructor) like this:

typeof(T).TypeInitializer.Invoke(null, null);

It needs both nulls. Specifying only one gives a MemberAccessException.

Thus, your code might want to look something like this:

internal static void ShowValue()
{
    if (_val == null)
    {
        if (typeof(T).TypeInitializer != null)
            typeof(T).TypeInitializer.Invoke(null, null);
        if (_val == null)
            throw new InvalidOperationException(string.Format("The type initializer of {0} did not initialize the _val field.", typeof(T)));
    }
    Console.WriteLine(_val);
}

And with that, you can remove the new() constraint.

我是有多爱你 2024-12-04 13:32:50

您无法控制静态构造函数何时执行,但可以保证它将在访问任何静态属性或方法之前以及实例化之前运行。

实际上没有理由希望静态构造函数提前执行。如果您没有使用类中的任何内容,但希望运行静态构造函数中的代码,那么您的设计就有问题。

You have no control of when the static constuctor will execute, but what is guaranteed is that it will run before accessing any static property or method and before instantiation.

There is really no reason to want the static constructor to execute at an earlier point. If you are not using anything from the class but you want the code in the static constructor to run, then something is wrong in your design.

秋风の叶未落 2024-12-04 13:32:50

静态构造函数是自动的,仅一次。你不能自己打电话给他们。

此处的示例:

public class Bus
{
    // Static constructor:
    static Bus()
    {
        System.Console.WriteLine("The static constructor invoked.");
    }    

    public static void Drive()
    {
        System.Console.WriteLine("The Drive method invoked.");
    }
}

class TestBus
{
    static void Main()
    {
        Bus.Drive();
    }
}

输出:

The static constructor invoked.

The Drive method invoked.

Static constructors are automatically, only once. You cannot call them yourself.

An example from here:

public class Bus
{
    // Static constructor:
    static Bus()
    {
        System.Console.WriteLine("The static constructor invoked.");
    }    

    public static void Drive()
    {
        System.Console.WriteLine("The Drive method invoked.");
    }
}

class TestBus
{
    static void Main()
    {
        Bus.Drive();
    }
}

output:

The static constructor invoked.

The Drive method invoked.
浪漫之都 2024-12-04 13:32:50

我引导您阅读有关 静态构造函数< 的 MSDN 文章/a> 以及页面下方大约 10% 的位置:

自动调用静态构造函数来初始化类
在创建第一个实例或任何静态成员之前
参考。

I direct you to the MSDN article on Static Constructors and about 10% down the page:

A static constructor is called automatically to initialize the class
before the first instance is created or any static members are
referenced.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文