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

53

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

我有一个由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...
      }
   }
}
13个回答

71

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

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

简而言之,如果您想保留生成类为您完成的重要构造函数内容(您应该这样做),那么在您的partial class中创建以下内容:

partial void OnCreated()
{
    // Do the extra stuff here;
}

3
现在出现了一个投票的困境......这实际上与OP的问题无关,因为它不涉及L2S,所以不会有OnCreated,但你已经阻止了我一直敲桌子,所以我认为应该给你一个赞。 - Ryan
@Ryan:很高兴能够帮到你。谢谢 :-) - Tom Chantler
有没有 WCF 客户端的等价物?它们被声明为部分类,但似乎没有 OnCreated 方法让我做任何事情。我是不是漏了什么?这真的很烦人。 - Doctor Jones

41

这是不可能的。

部分类本质上是同一类的组成部分;没有方法可以被定义两次或被覆盖,其中包括构造函数。

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


13
嗯,我想一个优雅的解决方案是以下这样:
//* 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
        }
    }
}

这种方法有一些缺点(就像其他任何东西一样):

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

  2. 如果需要为 MyCustomization 类实现 IDiposable 接口以正确处理 MyCustomization 类的非托管资源释放,我还不知道如何触发 MyCustomization.Dispose() 方法而不触及 AutogenCls.cs 文件...(但是我说“还不知道”:)

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

享受吧 :)


StyleCop 会对这个解决方案提出警告:你应该避免未使用的私有变量。 - Quark Soup
2
这个解决方案只提供静态构造函数:没有访问this的权限。 - Cœur
一个小简化,不需要声明 class MyCustomization,只需声明 Task _customization = TaskEx.Run(async () => { /* Do customization */ });。如果不需要,可以省略 async - Cœur

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.
}

它有一定的限制,在这种特殊情况下,当你需要修改一个生成的文件时,它可能不是正确的解决方案,但对于那些试图在部分类中重写功能的人来说,这很有帮助。


9
大问题在于自动生成的代码必须实现这一点,但在许多情况下,我无法控制自动生成的代码。 - VoteCoffee

3
该问题的原因是,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自动生成的代理执行初始化登录。

2
有时您无法访问或不允许更改默认构造函数,因此您不能使默认构造函数调用任何方法。
在这种情况下,您可以创建另一个带有虚拟参数的构造函数,并使该新构造函数使用“:this()”调用默认构造函数。
public SomeClass(int x) : this()
{
    //Your extra initialization here
}

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

SomeClass objSomeClass = new SomeClass(0);

2

您无法这样做。我建议使用部分方法,然后为其创建一个定义。例如:

public partial class MyClass{ 

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

    public partial void OnCreated();
}

其余部分应该很容易理解。
编辑:
我还要指出,您应该为此服务定义一个接口,然后编写程序将其编程,这样您就不必引用实际实现。 如果您这样做,那么您将有其他几个选项。

2
我想你可以使用PostSharp来完成这个任务,似乎已经有人为生成的部分类中的方法做了你想要的事情。我还没有尝试过将其转换为编写方法并替换构造函数的能力,但这似乎值得一试。
编辑:这也是相同的思路,看起来也很有趣。

1

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

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

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关键字,可以告诉编译器这些方法实际上是一个构造函数,并且所有包含的代码按照不保证的顺序运行。 - N-ate

0

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

using System.Runtime.Serialization;

partial class MyWebService
{
     [OnDeserialized]
     public void OnDeserialized(StreamingContext context)
     {
         // your code here
     }
}

这是为 web 服务还是为在服务调用中返回的对象进行反序列化?我尝试将其添加到我的 web 服务客户端的部分类中,但是我的方法没有被调用... - Adriaan Davel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接