繁体   English   中英

c#:在两个不同的上下文中重用部分类定义

[英]c#: re-use partial class definition in two different contexts

我试图重新使用公共部分类定义来定义几个不同的类。 这是描述所需结果的概念(非工作)代码:

namespace common {
    // Here is the part of the class which I want to re-use
    // literally in two different contexts
    partial class A {
        public void f() { Console.WriteLine(s); }
    }
}

namespace context1 {
    using common; // using as #include
    partial class A {
        String s = "contetx1";
    }
}

namespace context2 {
    using common; // using as #include
    partial class A {
        String s = "context2";
    }
}

class Program {
    static void Main(string[] args) {
        context1.A a1 = new context1.A();
        context2.A a2 = new context2.A();
        a1.f();
        a2.f();
        Console.ReadKey();
    }
}

context1和context2中的代码是自动生成的。 我想在自动生成的类中使用相同的手动编写的代码(上面的命名空间'common')。 目前我使用自动生成器脚本复制了常见的块,但是想知道在c#中有更优雅的方法吗?

为了澄清 - 上面的代码只是一个例子,我理解为什么它不起作用(不能跨命名空间拆分部分类定义)。 另外,虽然在上面的例子中,我可以只有一个基础公共类,实际上它确实需要是同一个类,我用部分定义实现。

---后来补充---

好的,在阅读完答案之后,我意识到我的问题的答案是“不”,我感谢你们确认我担心使用部分课程没有简单的方法。 让我试着详细说明我是如何提出这样的问题的。 也许你会指出一些简单而优雅的解决方案。 想象一下,有一个巨大的类描述了一些硬件模型以及模拟它的方法,它是从一些硬件描述自动生成的。 就像是:

partial class CPU1 : GenericCPU {
    ...
    partial class register123 { /*...fields and methods...*/ }
    ...
    partial class alu758 {
        /*...thousand fields and methods with partial hook method definitions...*/
        partial void start_get_data_hook();
        partial void finish_get_data_hook();
        void get_data() {
            start_get_data_hook();
            /*... some alu758-specific code for getting input data...*/
            finish_get_data_hook();
        }
        ...
    }
    ...
    void override simulate(); // Assume GenericCPU has virtual "simulate()"
}

假设没有任何其他东西,它模拟一些CPU(或其他)。 部分钩子方法和调用得到优化,每个人都很开心。 现在,我希望能够在自动生成的文件之外轻松定义钩子,我需要钩子才能访问它们各自的类字段和方法。 每次我想使用其中一个钩子时,为钩子需要的数据定义一个特殊的接口是不可能的。 假设我想在CPU1.alu758.get_data()的开头和结尾处做一些特殊的事情,它对于部分类和方法来说非常容易:

partial class CPU1 {
    partial class alu758 {
        /* some more fields which the hooks use */
        partial void start_get_data_hook() { /* do some preparation stuff */ }
        partial void finish_get_data_hook() { /* do some finalization stuff */ }
    }
}

虽然只有一个CPU1类,但它可以完美运行。 但是,假设有10个自动生成的CPU类CPU1..CPU10:

String processor_model = /* user input */;
GenericCPU cpu;
switch(processor_model) {
    case "CPU1": cpu = new CPU1; break;
    case "CPU2": cpu = new CPU2; break;
    ...
}
cpu.simulate()

现在,我希望能够做到这一点:能够使用相同的钩子代码修改所有十个CPUx类的行为(假设CPU1..10结构​​足够类似,允许这样做)。 我会用#include语句实现的东西,或者如果部分类允许一些语法将相同的代码拉到几个类中(例如在那个概念性的非工作示例中引出“为什么在地球上”的问题),或者我现在所做的事情一个脚本,可以在10个类中复制钩子代码。

这对c#来说太脏了吗?

我怀疑你是从C / C ++思维模式而不是C#语言中解决这个问题。 在早期的语言中,您可以通过#include以您希望的方式重用源代码,但C#更倾向于重新使用已编译的程序集而不是重新使用原始源。

除了从公共基类继承之外,向现有类(AFAIK)添加功能的唯一方法是使用扩展方法:

namespace context1
{
    public class A
    {
        internal String s = "context1";
    }
}

namespace context2
{
    public class A
    {
        internal String s = "context2";
    }
}

namespace common
{
    public static class ExtensionsForA
    {
        public static void f(this context1.A a) { common_f(a.s); }
        public static void f(this context2.A a) { common_f(a.s); }

        private static void common_f(string s) { Console.WriteLine(s); }
    }

    class Program
    {
        static void Main(string[] args)
        {
            context1.A a1 = new context1.A();
            context2.A a2 = new context2.A();
            a1.f();
            a2.f();
            Console.ReadKey();
        }
    }
}

不幸的是,这种方法带来了它自己的头痛,例如确保扩展方法可以访问所有必需的成员,并且必须为每个上下文维护适当版本的f(),所以我不一定推荐它。

也许如果您要向我们提供更具体的问题描述,我们可能会为您提供更优雅的解决方案。

您可以将公共代码委托给一个类。 然后只重复对代表的引用。 在你的例子中,这不会给你带来太大的收益,但在任何更复杂的情况下,它都会比较干。 您甚至可以在自定义部分中添加一个公共接口,只需转发来自委托的调用。 这样,接口和实现很常见,但可以逐个方法地轻松更改为独特的行为。

DRYer但不那么健壮(如果另一个部分扩展基础也不能是部分的):

namespace Common {
    abstract class A {
        private readonly string _s;
        protected A(string s){ _s = s; }
        public void F() { Console.WriteLine(_s); }
    }    
}

namespace Context1 {
    partial class A : Common.A {
        public A () : base ("context1");
    }
}

namespace Context2 {
    partial class A : Common.A {
        public A () : base ("context2");
    }
}

class Program {
    static void Main(string[] args) {
        Context1.A a1 = new Context1.A();
        Context2.A a2 = new Context2.A();
        a1.F();
        a2.F();
        Console.ReadKey();
    }
}

更强大,并将与已扩展基类的部分工作:

namespace Common {
    class A : IA {
        private readonly string _s;
        public A(string s){ _s = s; }
        public void F() { Console.WriteLine(_s); }
    }
    interface IA {
        void F();
    }     
}

namespace Context1 {
    partial class A : IA {
        private IA _a = new Common.A("context1");
        public void F(){return _a.F();}
    }
}

namespace Context2 {
    partial class A : IA {
        private IA _a = new Common.A("context2");
        public void F(){return _a.F();}
    }
}

class Program {
    static void Main(string[] args) {
        Context1.A a1 = new Context1.A();
        Context2.A a2 = new Context2.A();
        a1.F();
        a2.F();
        Console.ReadKey();
    }
}

C#是一种编译语言,因此,程序运行时所有符号名称都消失了; 唯一剩下的就是地址和抵消。 如果我们通过在第二个分类中的成员s之前添加一些东西,以下面的方式稍微修改你的程序; 我们看到它的偏移量在第二种情况下会有所不同:

namespace context1 {
    using common; // using as #include
    partial class A {
        String s = "contetx1";
    }
}

namespace context2 {
    using common; // using as #include
    partial class A {
        String a = "blablabla";
        String s = "context2";
    }
}

这就是为什么你的想法不起作用的原因因为函数public void f() { Console.WriteLine(s); } public void f() { Console.WriteLine(s); }的某个时候采取用于访问变量s偏移,但对于另一个部分类采取另一种代替偏移。 就编译器而言,即使它们具有相同的名称,您的两个变量s也是两个不同的变量,它们之间没有任何共同点,您也可以给它们两个不同的名称; 例如s1s2

如果您不想使用公共接口或复制代码,那么我看到的唯一剩下的解决方案是将这些公共变量及其相关代码分组到另一个对象中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM