不是按名称而是按类型指定接口成员

本文关键字:接口 成员 类型 | 更新日期: 2023-09-27 18:32:14

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

例如,我有类:具有Header属性和SimpleRequest1属性的类:SimpleRequest
另一个是拥有Header财产和ComplexRequest1财产的ComplexRequest

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

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

这里有一个可以带来更多澄清的耦合问题。

不是按名称而是按类型指定接口成员

编辑(在看到您的代码示例后):从技术上讲,您的代码没有 Header 属性,它有一个Header字段。这是一个重要的区别,因为您无法在接口中指定字段。但是,使用下面描述的方法,可以将属性添加到返回字段值的类中。


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

不可以,接口名称不能是动态的。无论如何,这样的接口不会很有用。如果你有一个类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; }
     }
}

您可以更一般地定义接口:

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;
    }
  }
}
暂时

搁置类和属性的命名。

如果要创建包含与特定 +1 类型相关的属性的接口,则有几个选项。

对 +1 使用基类

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

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

在接口上创建泛型属性

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

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

您可能会像这样使用:

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

更新

假设您的类是分部类,您始终可以创建包含您的接口的分部类的第二个(非计算机生成的)版本。

你提到了svcutil,所以我假设你使用这些类作为WCF数据合同?

如果是这种情况,那么您可以使用 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只是某个人在Microsoft写的剧本。 它不是魔法或"正确"或"标准"。 您的脚本可以调用 scvutil,然后对生成的文件进行一些快速更改。

 

编辑

(看到您的编辑后)

您已经在使用MessageBodyMemberAttributeName 属性,因此只需更改以下内容:

public string SimpleRequest1; 

public string Request1; 

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

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

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

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