【问题标题】:C# Alternative to virtual static methods and static sub classesC# 替代虚拟静态方法和静态子类
【发布时间】:2012-10-11 21:38:04
【问题描述】:

每当我阅读 RE 这个问题或类似的静态继承主题时,回复通常是不支持这(我们知道),并且给出的原因是因为这是一个糟糕的设计,并且可能存在更好的方法来做到这一点。我很想找到一种更好的方法,所以我愿意接受所有建议 - 这就是我想要做的。

我有一个没有实例数据的类。所有方法都是静态的。我们称之为class BaseStatic。我现在想要一个新的静态类(当然有几个,但我们坚持一个),它继承自这个静态类并添加一些新的静态方法,我们称之为SubStatic

我希望消费者能够写的是:

SubStatic.MethodFromSub();

还有

SubStatic.MethodFromBase();

我知道我也可以写:

BaseStatic.MethodFromBase()

明确地但是消费者必须知道哪个类实现了哪些方法。我不能通过继承来做到这一点,因为我不能从另一个静态类继承一个静态类。那么有什么更好的方法呢?

现在,我知道我可以将这些类作为实例类,并且我可以将所有方法定义为静态 - 这将给我上面描述的行为,但会导致其他问题,即:

  1. 当我这样做时:SubStatic.MethodFromBase() SubStatic 静态构造函数未被调用,因为该方法在父静态类中运行(调用父静态构造函数)

  2. 如果一个静态父方法需要调用另一个子类可以覆盖的方法,我需要子类中的一个虚拟静态方法。我知道我不能拥有。

显然设计如此糟糕 - 谁能帮我重做它?我知道我可以使用实例继承并正确使用虚拟方法(我已经以这种方式工作)但是客户端代码总是必须创建一个实例(或者我想是一些单例)。

【问题讨论】:

  • 您可以使用单例设计模式来创建实例并将它们返回给用户。这不能帮助您解决问题吗?
  • 是的 - 但是我仍然在创建一个实例,不是吗?另外,消费者必须写:SomeClass.Instance.SomeMethod(),对吧?
  • 是的,您必须创建实例,但只需创建一次,然后您就可以使用延迟加载模式缓存它们。这是您可以继承的唯一方法。此外,这将导致更灵活的代码。
  • 是否有必要只运行该特定类的 cctor(使用限定符而不是基础的那个)?或者如果所有加载的实现的 cctors 都运行就可以了吗?
  • 我可以用任何一种方式工作......

标签: c# inheritance static virtual


【解决方案1】:

这可以满足您的目的,但我当然会包括一些异常处理,并在其实现中附带大量文档,说明其工作原理和方式。

Base 的静态构造函数运行(一次)时,应用程序域中当前加载的所有程序集都会被编入目录,并选择派生自Base 的类型。遍历这些,我们运行静态构造函数。值得注意的是,这不再保证每个实现的 cctor 将只运行一次,必须将逻辑添加到每个实现中以重新做出该断言。此外,在运行 Base 的 cctor 之后加载的类型不会通过调用 Base 中的方法来初始化

要模拟虚拟方法,请使用new 关键字来隐藏基本方法。您可以通过使用声明类的名称对其进行限定来调用基方法(如示例中的类B

using System;
using System.Linq;
using System.Runtime.CompilerServices;

namespace ConsoleApplication6
{
    public class Base
    {
        static Base()
        {
            Console.WriteLine("Base cctor");

            var thisType = typeof (Base);
            var loadedTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes());
            var derivations = loadedTypes.Where(thisType.IsAssignableFrom);

            foreach(var derivation in derivations)
            {
                RuntimeHelpers.RunClassConstructor(derivation.TypeHandle);
            }
        }

        public static void Foo()
        {
            Console.WriteLine("Bar");
        }
    }

    public class A : Base
    {
        static A()
        {
            Console.WriteLine("A cctor");
        }
    }

    public class B : Base
    {
        static B()
        {
            Console.WriteLine("B cctor");
        }

        public new static void Foo()
        {
            Console.WriteLine("Bar!!");
            Base.Foo();
        }
    }

    class Program
    {
        static void Main()
        {
            Console.WriteLine("A:");
            A.Foo();
            Console.WriteLine();
            Console.WriteLine("B:");
            B.Foo();
            Console.WriteLine();
            Console.WriteLine("Base:");
            Base.Foo();
            Console.ReadLine();
        }
    }
}

编辑

另一个选择在于 CRTP(或 C# 范例中的 CRGP)或奇怪的重复模板(通用)参数模式

using System;
using System.Runtime.CompilerServices;

namespace ConsoleApplication6
{
    public class Base<T>
        where T : Base<T>
    {
        static Base()
        {
            RuntimeHelpers.RunClassConstructor(typeof (T).TypeHandle);
        }

        public static void Foo()
        {
            Console.WriteLine("Bar");
        }
    }

    public class Base : Base<Base>
    {
    }

    public class A : Base<A>
    {
        static A()
        {
            Console.WriteLine("A cctor");
        }
    }

    public class B : Base<B>
    {
        static B()
        {
            Console.WriteLine("B cctor");
        }

        public new static void Foo()
        {
            Console.WriteLine("Bar!!");
            Base<B>.Foo();
        }
    }

    class Program
    {
        static void Main()
        {
            Console.WriteLine("A:");
            A.Foo();
            Console.WriteLine();
            Console.WriteLine("B:");
            B.Foo();
            Console.WriteLine();
            Console.WriteLine("Base:");
            Base.Foo();
            Console.ReadLine();
        }
    }
}

在这种情况下,当我们在 A 上调用静态方法时,我们实际上是在 Base&lt;A&gt; 上调用它,这与 Base&lt;B&gt;Base 不同,因此我们可以实际确定如何调用该方法并运行适当的 cctor。

【讨论】:

  • 嗯——非常聪明,但我会看到一个问题是所有静态子类构造函数都会被调用——我可能有 100 个这样的子类,这会很浪费。有趣的是,人们是在提出实现这项工作的方法,而不是告诉我为什么设计不好。
  • @RBrowning99 这正是我问你是否可以调用所有派生类 cctor 的原因;)
  • 我误解了你的问题 - 我以为你在问我是否关心是否调用了父类或子类构造函数。对不起。
  • @RBrowning99 添加了另一个选项,该选项处理大量派生计数的可能性,但增加了一些复杂性
  • 这看起来很棒 - 我明天有空的时候会玩,可能就是我所需要的。
【解决方案2】:

您可以通过使用泛型来实现这一点。例如,您可以使用类似的东西:

public class MainStatic<T> where T : MainStatic<T>
{
    public static void Foo()
    {
    }

    static MainStatic()
    {
        RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
    }
}

public class SubStatic : MainStatic<SubStatic>
{
    public static void Bar()
    {
    }
}

public class Instance
{
    public void FooBar()
    {
        SubStatic.Foo();
        SubStatic.Bar();
    }
}

【讨论】:

  • 当您在 SubStatic 上调用 Foo 时,不会调用 SubStatic 上的静态构造函数 (cctor)。这是 OP 的问题之一,如何在调用来自其基的静态成员时强制 cctor 运行。
  • 确实,我的错我没有检查这个。我看看能不能改一下。
  • 看我的回答,你只需拨打RuntimeHelpers.RunClassConstructor(typeof (T).TypeHandle);
  • @mlorbetske 确实,刚刚更新了我的答案。实际上,您已经在答案的第二部分中得到了它,而我的答案并没有增加多少价值。
猜你喜欢
  • 2011-02-12
  • 2010-11-17
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 2010-12-21
相关资源
最近更新 更多