C#替代虚拟静态方法和静态子类

时间:2012-10-22 05:16:26

标签: c# inheritance static virtual

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

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

我希望消费者能够写下:

SubStatic.MethodFromSub();

以及

SubStatic.MethodFromBase();

我知道我也可以写:

BaseStatic.MethodFromBase()

显式,但消费者必须知道哪个类实现了哪些方法。我不能用继承来做这个,因为我不能从另一个继承一个静态类。那么有什么更好的方法呢?

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

  1. 执行此操作时:SubStatic.MethodFromBase()未调用SubStatic静态构造函数,因为该方法在父静态类中运行(调用父级的静态构造函数)

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

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

2 个答案:

答案 0 :(得分:4)

这可以达到您的目的,但我当然会包含一些异常处理,并伴随其实施,提供大量文档,说明原因和工作原理。

运行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<A>上调用它,这与Base<B>Base不同,所以我们可以实际确定如何调用该方法并运行相应的cctor。

答案 1 :(得分:2)

您可以使用Generics实现此目的。例如,你可以使用类似的东西:

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();
    }
}