不是按名称而是按类型指定接口成员
本文关键字:接口 成员 类型 | 更新日期: 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数据合同?
如果是这种情况,那么您可以使用 DataMemberAttribute
的 name
属性
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,然后对生成的文件进行一些快速更改。
编辑
(看到您的编辑后)
您已经在使用MessageBodyMemberAttribute
的 Name
属性,因此只需更改以下内容:
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); }
}
}
如果反射证明太慢,您可以使用表达式树为此构建委托,但我会坚持一个简单的解决方案。
这样做的好处是你只需要编写一次这段代码 - 但它确实意味着围绕真正的请求对象创建一个包装器,而分部类回答不需要。