用另一个部分类覆盖部分类的默认构造函数

Posted

技术标签:

【中文标题】用另一个部分类覆盖部分类的默认构造函数【英文标题】:Override Default Constructor of Partial Class with Another Partial Class 【发布时间】:2010-09-19 20:29:48 【问题描述】:

我认为这是不可能的,但如果是的话,我需要它:)

我有一个通过 Visual Studio 2008 的 wsdl.exe 命令行工具自动生成的代理文件。

代理输出是部分类。我想覆盖生成的默认构造函数。我宁愿不修改代码,因为它是自动生成的。

我尝试创建另一个部分类并重新定义默认构造函数,但这不起作用。然后我尝试使用 override 和 new 关键字,但这不起作用。

我知道我可以从部分类继承,但这意味着我必须更改我们所有的源代码以指向新的父类。我宁愿不必这样做。

有什么想法、解决方法或技巧吗?

//Auto-generated class
namespace MyNamespace 
   public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol 
      public MyWebService() 
         string myString = "auto-generated constructor";
         //other code...
      
   


//Manually created class in order to override the default constructor
namespace MyNamespace 
   public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol 
      public override MyWebService()  //this doesn't work
         string myString = "overridden constructor";
         //other code...
      
   

【问题讨论】:

【参考方案1】:

我遇到了类似的问题,我生成的代码是由 DBML 文件创建的(我正在使用 Linq-to-SQL 类)。

在生成的类中,它在构造函数的末尾调用了一个名为 OnCreated() 的部分 void。

长话短说,如果您想保留生成的类为您执行的重要构造函数(您可能应该这样做),那么在您的部分类中创建以下内容:

partial void OnCreated()

    // Do the extra stuff here;

【讨论】:

现在这是一个投票困境...与 OP 问题没有任何关系,这与 L2S 无关,所以不会有 OnCreated 但你阻止了我把头撞在桌子上所以 +1 我想。 @Ryan:很高兴能帮上忙。谢谢:-) WCF 客户端是否有任何等价物?它们被声明为部分类,但似乎没有 OnCreated 方法允许我做任何事情。有什么我想念的吗?这很烦人。【参考方案2】:

这是不可能的。 部分类本质上是同一类的一部分;任何方法都不能定义两次或重写,包括构造函数。

您可以在构造函数中调用一个方法,并且只在其他部分文件中实现它。

【讨论】:

【参考方案3】:

嗯, 我认为一个优雅的解决方案如下:

//* AutogenCls.cs file
//* Let say the file is auto-generated ==> it will be overridden each time when
//* auto-generation will be triggered.
//*
//* Auto-generated class, let say via xsd.exe
//*
partial class AutogenCls

    public AutogenCls(...)
    
    




//* AutogenCls_Cunstomization.cs file
//* The file keeps customization code completely separated from 
//* auto-generated AutogenCls.cs file.
//*
partial class AutogenCls

    //* The following line ensures execution at the construction time
    MyCustomization m_MyCustomizationInstance = new MyCustomization ();

    //* The following inner&private implementation class implements customization.
    class MyCustomization
    
        MyCustomization ()
        
            //* IMPLEMENT HERE WHATEVER YOU WANT TO EXECUTE DURING CONSTRUCTION TIME
        
    

这种方法有一些缺点(就像一切一样):

    在AutogenCls类的整个构建过程中,MyCustomization内部类的构造函数具体何时执行尚不清楚。

    如果需要为 MyCustomization 类实现 IDiposable 接口以正确处理 MyCustomization 类的非托管资源的处置,我不知道(尚)如何触发 MyCustomization.Dispose() 方法触摸 AutogenCls.cs 文件...(但正如我所说的'还':)

但是这种方法提供了与自动生成代码的良好分离 - 整个定制在不同的 src 代码文件中分离。

享受:)

【讨论】:

StyleCop 会抱怨这个解决方案:你应该避免使用未使用的私有变量。 此方案仅提供静态构造函数:无法访问this 一个小简化,不用class MyCustomization,只需要声明Task _customization = TaskEx.Run(async () => /* Do customization */ );。不需要async可以省略。【参考方案4】:

实际上,这现在是可能的,现在已经添加了部分方法。这是文档:

http://msdn.microsoft.com/en-us/library/wa80x488.aspx

基本上,这个想法是您可以在定义分部类的文件中声明和调用方法,但实际上不能在该文件中定义方法。然后,您可以在另一个文件中定义该方法。如果您正在构建一个未定义方法的程序集,那么 ORM 将删除对该函数的所有调用。

所以在上面的例子中它看起来像这样:

//自动生成的类

namespace MyNamespace 
   public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol 
      public MyWebService() 
         string myString = "auto-generated constructor";
         OtherCode();
      
   


partial void OtherCode();

//为了覆盖默认构造函数而手动创建的类

partial void OtherCode()

   //do whatever extra stuff you wanted.

它有些限制,在这种特殊情况下,如果您有一个需要更改的生成文件,它可能不是正确的解决方案,但对于其他偶然发现此尝试覆盖部分类中的功能的人来说,这会很有帮助。

【讨论】:

最大的问题是自动生成的代码必须实现这一点,但在很多情况下我无法控制自动生成的代码【参考方案5】:

OP 遇到的问题是 Web 引用代理没有生成任何可用于拦截构造函数的部分方法。

我遇到了同样的问题,我不能只升级到 WCF,因为我的目标 Web 服务不支持它。

我不想手动修改自动生成的代码,因为如果有人调用代码生成,它会变得扁平化。

我从不同的角度解决了这个问题。我知道我的初始化需要在请求之前完成,实际上并不需要在构造时完成,所以我只是像这样覆盖了 GetWebRequest 方法。

protected override WebRequest GetWebRequest(Uri uri)

    //only perform the initialization once
    if (!hasBeenInitialized)
    
        Initialize();
    

    return base.GetWebRequest(uri);


bool hasBeenInitialized = false;

private void Initialize()

    //do your initialization here...

    hasBeenInitialized = true;

这是一个很好的解决方案,因为它不涉及破解自动生成的代码,并且它适合 OP 为 SoapHttpClientProtocol 自动生成的代理执行初始化登录的确切用例。

【讨论】:

【参考方案6】:

你不能这样做。我建议使用部分方法,然后您可以为其创建定义。比如:

public partial class MyClass 

    public MyClass()  
        ... normal construction goes here ...
        AfterCreated(); 
    

    public partial void OnCreated();

其余的应该是不言自明的。

编辑:

我还想指出,您应该为此服务定义一个接口,然后您可以对其进行编程,因此您不必引用实际的实现。如果您这样做了,那么您还有其他一些选择。

【讨论】:

【参考方案7】:

我想你也许可以用PostSharp 做到这一点,看起来有人已经做了你want for methods in generated partial classes 的事情。我不知道这是否会很容易转化为编写方法并让它的主体替换构造函数的能力,因为我还没有尝试过,但似乎值得一试。

编辑:this is along the same lines,看起来也很有趣。

【讨论】:

【参考方案8】:

有时你没有访问权限或不允许更改默认构造函数,因此你不能让默认构造函数调用任何方法。

在这种情况下,您可以使用虚拟参数创建另一个构造函数,并使用“:this()”使这个新构造函数调用默认构造函数

public SomeClass(int x) : this()

    //Your extra initialization here

当您创建此类的新实例时,您只需像这样传递虚拟参数:

SomeClass objSomeClass = new SomeClass(0);

【讨论】:

【参考方案9】:

在我看来,这是该语言的设计缺陷。他们应该允许一个部分方法的多个实现,这将提供一个很好的解决方案。 以一种更好的方式,构造函数(也是一个方法)也可以简单地标记为部分构造函数,并且在创建对象时会运行具有相同签名的多个构造函数。

最简单的解决方案可能是为每个额外的部分类添加一个部分“构造函数”方法:

public partial class MyClass 

    public MyClass()  
        ... normal construction goes here ...
        OnCreated1(); 
        OnCreated2(); 
        ...
    

    public partial void OnCreated1();
    public partial void OnCreated2();

如果您希望部分类彼此不可知,您可以使用反射:

// In MyClassMyAspect1.cs
public partial class MyClass 

    public void MyClass_MyAspect2()  
        ... normal construction goes here ...

    



// In MyClassMyAspect2.cs
public partial class MyClass 

    public void MyClass_MyAspect1()  
        ... normal construction goes here ...
    


// In MyClassConstructor.cs
public partial class MyClass : IDisposable  

    public MyClass()  
       GetType().GetMethods().Where(x => x.Name.StartsWith("MyClass"))
                             .ForEach(x => x.Invoke(null));
    

    public void Dispose() 
       GetType().GetMethods().Where(x => x.Name.StartsWith("DisposeMyClass"))
                             .ForEach(x => x.Invoke(null));
    


但实际上他们应该添加更多的语言结构来处理部分类。

【讨论】:

允许在具有相同签名的构造函数上使用 partial 关键字可以向编译器表明这些方法实际上是一个构造函数,所有包含的代码都以无保证的顺序运行。【参考方案10】:

对于由 Visual Studio 生成的 Web 服务代理,您不能在分部类中添加自己的构造函数(当然可以,但它不会被调用)。相反,您可以使用 [OnDeserialized] 属性(或 [OnDeserializing])在 Web 代理类实例化的位置挂接您自己的代码。

using System.Runtime.Serialization;

partial class MyWebService

     [OnDeserialized]
     public void OnDeserialized(StreamingContext context)
     
         // your code here
     

【讨论】:

这是针对 Web 服务还是针对在服务调用中返回时被反序列化的对象?我尝试将它添加到我的 Web 服务客户端的部分类中,但我的方法没有被调用...【参考方案11】:

我并没有完全解决 OP,但是如果您碰巧使用 EntityFramework Reverse POCO 生成器生成类,则在构造函数中调用了一个部分方法,该方法可以方便地初始化您通过部分类添加的内容拥有...

由工具生成:

   [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.3.0")]
    public partial class Library 
        public string City  get; set; 
        public Library() 
            InitializePartial();
        
        partial void InitializePartial();
    

由您添加:

    public partial class Library 
        List<Book> Books  get; set; 
        partial void InitializePartial() 
            Books = new List<Book>();
        
    

    public class Book 
        public string Title  get; set; 
    

【讨论】:

【参考方案12】:

没有什么我能想到的。我能想到的“最佳”方法是添加一个带有虚拟参数的 ctor 并使用它:

public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol 

   public override MyWebService(int dummy) 
    
         string myString = "overridden constructor";
         //other code...
   



MyWebService mws = new MyWebService(0);

【讨论】:

以上是关于用另一个部分类覆盖部分类的默认构造函数的主要内容,如果未能解决你的问题,请参考以下文章

类和对象

构造函数的分类及调用

C++ 文件读写

覆盖部分类中的虚拟方法

构造函数

具有非默认构造函数的 C++ 继承