C#泛型:我可以约束一组没有实现接口的类吗?

时间:2022-09-02 09:28:59

I have 3 classes that are essentially the same but don't implement an interface because they all come from different web services.

我有3个基本相同但没有实现接口的类,因为它们都来自不同的Web服务。

e.g.

  • Service1.Object1
  • Service2.Object1
  • Service3.Object1

They all have the same properties and I am writing some code to map them to each other using an intermediary object which implements my own interface IObject1

它们都具有相同的属性,我正在编写一些代码,使用实现我自己的接口IObject1的中间对象将它们相互映射。

I've done this using generics

我使用泛型完成了这个

public static T[] CreateObject1<T>(IObject1[] properties)
  where T : class, new()
{
   //Check the type is allowed
   CheckObject1Types("CreateObject1<T>(IObject1[])", typeof(T));
   return CreateObjectArray<T>(properties);
}

private static void CheckObject1Types(string method, Type type)
{
  if (type == typeof(Service1.Object1)
  || type == typeof(Service2.Object1)
  || type == typeof(Service3.Object1)
  || type == typeof(Service1.Object1[])
  || type == typeof(Service2.Object1[])
  || type == typeof(Service3.Object1[]))
  {
     return;
  }

  throw new ArgumentException("Incorrect type passed to ServiceObjectFactory::" + method + ". Type:" + type.ToString());
}

My client code looks like:

我的客户端代码如下:

//properties is an array of my intermediary objects
Object1[] props = ServiceObjectFactory.CreateObject1<Object1>(properties);

What I want to do is get rid of the CheckObject1Types method and use constraints instead so that I get a build error if the types aren't valid, because at the moment I can call this method with any type and the ArgumentException is thrown by the CheckObject1Types method.

我想要做的是摆脱CheckObject1Types方法并使用约束,这样如果类型无效就会出现构建错误,因为目前我可以用任何类型调用此方法,并且抛出ArgumentException CheckObject1Types方法。

So I'd like to do something like:

所以我想做一些事情:

public static T[] CreateObject1<T>(IObject1[] properties)
  where T : class, new(), Service1.Object1|Service2.Object1|Service3.Object1
{
   return CreateObjectArray<T>(properties);
}

Any ideas?

Edit: I don't want to change the Reference.cs files for each webservice because all it takes is a team mate to update the web reference and BAM! broken code.

编辑:我不想更改每个Web服务的Reference.cs文件,因为只需要一个队友来更新Web引用和BAM!破碎的代码。

4 个解决方案

#1


14  

Assuming the generated classes are partial, you can create an interface and then add another partial source file to make your generated classes implement the interface. Then you can constrain by interface as normal. No changes to the actual generated code required :)

假设生成的类是部分的,您可以创建一个接口,然后添加另一个部分源文件,以使生成的类实现该接口。然后你可以正常约束接口。无需更改实际生成的代码:)

#2


1  

Constraining to a list of classes in an "OR" fashion like you want to do isn't possible in C#. (In fact, I'm not even sure it's legal directly in IL either.)

在C#中不可能像你想要的那样以“OR”方式约束到类列表。 (事实上​​,我甚至不确定它在IL中也是合法的。)

Your only option is to keep using the checktypes style functions. If you own the code for the different webservices, you can also implement a "sentinel" interface and use that as your constraint. I know sentinel interfaces are not recommended practice per the Framework Design Guidelines, but they occasionaly have their uses (this being one of them).

您唯一的选择是继续使用checktypes样式函数。如果您拥有不同Web服务的代码,您还可以实现“sentinel”接口并将其用作约束。我知道根据框架设计指南不推荐使用哨兵界面,但它们偶尔会有它们的用途(这就是其中之一)。

As Jon points out, you may be able to make use of prtial classes in order to implement a common interface. If your References.cs implements a class:

正如Jon指出的那样,您可以使用prtial类来实现通用接口。如果您的References.cs实现了一个类:

namespace TestServices
{
   internal partial class Service1SoapClient : System.ServiceModel.ClientBase<T>, K
   {
   }
}

You would then create another partial class in the same namespace (call it References.CommonInterface.cs), which can live anywhere in your project, with the following:

然后,您将在同一名称空间中创建另一个部分类(称为References.CommonInterface.cs),它可以存在于项目的任何位置,具有以下内容:

namespace TestServices
{
   internal interface ICommon
   {
   }

   internal partial class Service1SoapClient : ICommonInterface
   {
   }
}

#3


0  

I would write a converter class that took either of your three objects into a new object that supports the interface you want. Furthermore, I would use reflection so you don't have to type all the assignments manually (unless it's a small object and is not expected to change too much).

我会写一个转换器类,它将你的三个对象中的任何一个带入一个支持你想要的接口的新对象。此外,我会使用反射,因此您不必手动键入所有分配(除非它是一个小对象,并且预计不会改变太多)。

Using Reflection would also give you the guarantee that you want to make sure the objects implement whatever properties your new Interfaced object implements, otherwise when a property you expect is not implemented you could have it throw an error.

使用Reflection还可以保证您希望确保对象实现新Interfaced对象实现的任何属性,否则当您期望的属性未实现时,您可能会抛出错误。

#4


-2  

If you're pulling these objects from a web service you ulitmately do have control over the class definitions used. They don't just spring up out of thin air (even if you do have code generator or visual studio creating them initially). There's still a class file for each of them somewhere that must be compiled with the app, and you should be able to add your common interface to those class definitions.

如果您从Web服务中提取这些对象,则可以完全控制所使用的类定义。它们不仅仅是凭空创造的(即使你最初有代码生成器或visual studio创建它们)。在某些地方仍然有一个类文件必须使用应用程序进行编译,您应该能够将这些公共接口添加到这些类定义中。

#1


14  

Assuming the generated classes are partial, you can create an interface and then add another partial source file to make your generated classes implement the interface. Then you can constrain by interface as normal. No changes to the actual generated code required :)

假设生成的类是部分的,您可以创建一个接口,然后添加另一个部分源文件,以使生成的类实现该接口。然后你可以正常约束接口。无需更改实际生成的代码:)

#2


1  

Constraining to a list of classes in an "OR" fashion like you want to do isn't possible in C#. (In fact, I'm not even sure it's legal directly in IL either.)

在C#中不可能像你想要的那样以“OR”方式约束到类列表。 (事实上​​,我甚至不确定它在IL中也是合法的。)

Your only option is to keep using the checktypes style functions. If you own the code for the different webservices, you can also implement a "sentinel" interface and use that as your constraint. I know sentinel interfaces are not recommended practice per the Framework Design Guidelines, but they occasionaly have their uses (this being one of them).

您唯一的选择是继续使用checktypes样式函数。如果您拥有不同Web服务的代码,您还可以实现“sentinel”接口并将其用作约束。我知道根据框架设计指南不推荐使用哨兵界面,但它们偶尔会有它们的用途(这就是其中之一)。

As Jon points out, you may be able to make use of prtial classes in order to implement a common interface. If your References.cs implements a class:

正如Jon指出的那样,您可以使用prtial类来实现通用接口。如果您的References.cs实现了一个类:

namespace TestServices
{
   internal partial class Service1SoapClient : System.ServiceModel.ClientBase<T>, K
   {
   }
}

You would then create another partial class in the same namespace (call it References.CommonInterface.cs), which can live anywhere in your project, with the following:

然后,您将在同一名称空间中创建另一个部分类(称为References.CommonInterface.cs),它可以存在于项目的任何位置,具有以下内容:

namespace TestServices
{
   internal interface ICommon
   {
   }

   internal partial class Service1SoapClient : ICommonInterface
   {
   }
}

#3


0  

I would write a converter class that took either of your three objects into a new object that supports the interface you want. Furthermore, I would use reflection so you don't have to type all the assignments manually (unless it's a small object and is not expected to change too much).

我会写一个转换器类,它将你的三个对象中的任何一个带入一个支持你想要的接口的新对象。此外,我会使用反射,因此您不必手动键入所有分配(除非它是一个小对象,并且预计不会改变太多)。

Using Reflection would also give you the guarantee that you want to make sure the objects implement whatever properties your new Interfaced object implements, otherwise when a property you expect is not implemented you could have it throw an error.

使用Reflection还可以保证您希望确保对象实现新Interfaced对象实现的任何属性,否则当您期望的属性未实现时,您可能会抛出错误。

#4


-2  

If you're pulling these objects from a web service you ulitmately do have control over the class definitions used. They don't just spring up out of thin air (even if you do have code generator or visual studio creating them initially). There's still a class file for each of them somewhere that must be compiled with the app, and you should be able to add your common interface to those class definitions.

如果您从Web服务中提取这些对象,则可以完全控制所使用的类定义。它们不仅仅是凭空创造的(即使你最初有代码生成器或visual studio创建它们)。在某些地方仍然有一个类文件必须使用应用程序进行编译,您应该能够将这些公共接口添加到这些类定义中。