【问题标题】:Specify interface member not by name but type不是按名称而是按类型指定接口成员
【发布时间】:2012-05-06 23:15:33
【问题描述】:

我有很多由 svcutil 从一些外部 WSDL 文件生成的类似类。任何类都有一个名为class name + "1"Header 属性和string 属性。

例如,我有类:SimpleRequest 具有 Header 属性和 SimpleRequest1 属性。
另一个是ComplexRequest,它具有Header 属性和ComplexRequest1 属性。

所以,我想为这些类创建一个通用接口。所以,基本上我可以这样定义:

interface ISomeRequestClass {
  string Header;
  // here is some definition for `class name + "1"` properties...
}

是否可以在接口中定义这样的成员?


这里是后期编辑...

这是生成类的示例:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
public partial class SimpleRequest
{


    public string Header;

    [System.ServiceModel.MessageBodyMemberAttribute(Name="SimpleRequest", Namespace="data", Order=0)]
    public SimpleRequestMsg SimpleRequest1;

    public SimpleRequest()
    {
    }

    public SimpleRequest(string Header, SimpleRequestMsg SimpleRequest1)
    {
        this.Header = Header;
        this.SimpleRequest1 = SimpleRequest1;
    }
}

后编辑 2

我更改了这个烦人的 +1 属性的定义以表示真实的实际图片。它都有不同的类类型。那么如何将其拉出到通用界面呢?


后编辑 3

这里是coupled question,可以带来更多澄清。

【问题讨论】:

  • 你能编辑这些类吗?
  • 我可以编辑生成的类,但是如果将来我想重新生成这些类,我应该再做一次常规工作。

标签: c# .net oop class interface


【解决方案1】:

EDIT(查看您的代码示例后):从技术上讲,您的代码没有Header 属性,它有一个Header 字段。这是一个重要的区别,因为you cannot specify fields in an interface。但是,使用下面描述的方法,您可以将属性添加到返回字段值的类中。


是否可以在接口中定义这样的成员?

不,接口名称不能是动态的。无论如何,这样的界面不会很有用。如果您有一个 ISomeRequestClass 类的实例,您会使用什么名称来访问该属性?

但是,您可以使用显式接口实现:

interface ISomeRequestClass {
    string Header { get; set; }
    string ClassName1 { get; set; }
}

class SomeClass : ISomeRequestClass {
     string Header { ... }

     string SomeClass1 { ... }

     // new: explicit interface implementation
     string ISomeRequestClass.ClassName1 {
          get { return SomeClass1; }
          set { SomeClass1 = value; }
     }
}

【讨论】:

    【解决方案2】:

    您可以更一般地定义您的界面:

    interface ISomeRequestClass {
      string HeaderProp {get; set;}
      string Prop {get; set;}
    }
    

    您的具体类可以通过将接口成员映射到类字段来扩展(在额外的代码文件中),如下所示:

    public partial class SimpleRequest : ISomeRequestClass
    {
      public string HeaderProp
      {
        get
        {
          return Header;
        }
        set
        {
          Header = value;
        }
      }
    
      public string Prop
      {
        get
        {
          return SimpleRequest1;
        }
        set
        {
          SimpleRequest1= value;
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      暂时搁置类和属性的命名。

      如果您希望创建具有与您的特定 +1 类型相关的属性的界面,您有多种选择。

      为您的 +1 使用基类

      如果您的两个 +1 类都继承自同一个基类,您可以在接口定义中使用它:

      public interface IFoo
      {
       [...]
       PlusOneBaseType MyPlusOneObject{get;set;}
      }
      

      在您的界面上创建通用属性

      此方法允许您将 +1 属性的类型指定为通用参数:

      public interface IFoo<TPlusOneType>
      {
       [...]
       TPlusOneType MyPlusOneObject{get;set;}
      }
      

      你可能会这样使用:

      public class SimpleRequest : IFoo<SimpleRequest1>
      {
       [...]
      }
      

      更新

      鉴于您的类是分部类,您总是可以创建第二个(非机器生成的)分部类版本来实现您的接口。

      【讨论】:

      • 请查看我的问题编辑。这些类没有基类,但它们是部分的。
      【解决方案4】:

      您提到了 svcutil,所以我假设您将这些类用作 WCF DataContracts?

      如果是这种情况,那么您可以使用DataMemberAttributename 属性。

      interface IRequest 
      {
          string Header { get; set; }
          string Request1 { get; set; }
      }
      
      [DataContract]
      class SimpleRequest : IRequest
      {
          [DataMember]
          public string Header { get; set; }
      
          [DataMember(Name="SimpleRequest1"]
          public string Request1 { get; set; }
      }
      
      [DataContract]
      class ComplexRequest : IRequest
      {
          [DataMember]
          public string Header { get; set; }
      
          [DataMember(Name="ComplexRequest1"]
          public string Request1 { get; set; }
      }
      

      如果您担心在将来某个时间重新生成代码时会给自己更多的工作,那么我建议您编写一个 PowerShell 脚本来自动执行此转换。毕竟 svcutil 只是微软某个人编写的脚本。这不是魔术或“正确”或“标准”。您的脚本可以调用 scvutil,然后对生成的文件进行一些快速更改。

       

      编辑(看到您的编辑后)

      您已经在使用MessageBodyMemberAttributeName 属性,所以只需更改这个:

      public string SimpleRequest1; 
      

      public string Request1; 
      

      【讨论】:

        【解决方案5】:

        您真的需要这些类 来拥有一个通用接口吗?我很想创建一个 wrapper 接口(或只是一个具体的类),然后可以使用反射来访问相关字段:

        // TODO: Make this class implement an appropriate new interface if you want
        // to, for mocking purposes.
        public sealed class RequestWrapper<TRequest, TMessage>
        {
            private static readonly FieldInfo headerField;
            private static readonly FieldInfo messageField;
        
            static RequestWrapper()
            {
                // TODO: Validation
                headerField = typeof(TRequest).GetField("Header");
                messageField = typeof(TRequest).GetField(typeof(TRequest).Name + "1");
            }
        
            private readonly TRequest;
        
            public RequestWrapper(TRequest request)
            {
                this.request = request;
            }
        
            public string Header
            {
                get { return (string) headerField.GetValue(request); }
                set { headerField.SetValue(request, value); }
            }
        
            public TMessage Message
            {
                get { return (TMessage) messageField.GetValue(request); }
                get { messageField.SetValue(request, value); }
            }
        }
        

        如果反射证明太慢,您可以使用表达式树为此构建委托,但我会坚持从一个简单的解决方案开始。

        这样做的好处是您只需要编写此代码一次 - 但这确实意味着围绕真实请求对象创建一个包装器,而部分类不会回答。

        【讨论】:

        • 谢谢乔恩!实际上,您指的是我目前部分实施的方式。它还使用反射。我遇到的问题是如何使用反射创建新的常见对象?
        • @kseen:好吧,我们需要了解更多关于上下文的信息……您可以使用 Type.MakeGenericType 然后创建一个实例,但是您知道在那一点?你知道类型吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-02
        • 1970-01-01
        • 2021-12-29
        相关资源
        最近更新 更多