【问题标题】:C# Generics: Can I constrain to a set of classes that don't implement an interface?C# 泛型:我可以限制为一组不实现接口的类吗?
【发布时间】:2008-10-20 15:47:26
【问题描述】:

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

例如

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

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

我已经使用泛型完成了这项工作

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

我的客户端代码如下:

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

我想要做的是摆脱 CheckObject1Types 方法并改用约束,以便在类型无效时得到构建错误,因为目前我可以使用任何类型调用此方法并且 ArgumentException 是由 CheckObject1Types 方法抛出。

所以我想做这样的事情:

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

有什么想法吗?

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

【问题讨论】:

  • 我刚刚注意到我可以通过将检查类型代码从 && 更改为 || 来提高效率和 != 到 == 在任何人指出这一点之前。

标签: c# .net generics


【解决方案1】:

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

【讨论】:

  • 这就是我想要达到的目的。
  • 关于部分类技巧的要点。我刚刚查看了一个使用服务引用在 VS2008 (.NET 3.5) 中创建的新项目,它确实将客户端创建为部分类。
  • 有趣...我试试看。
  • 好主意。我没有意识到您实际上可以访问服务代理的源代码。这将是您问题的理想解决方案。给我点赞
  • 乔恩,你有没有在你的(很棒的)网站上发布过这样的例子?我很想看看。
【解决方案2】:

在 C# 中不可能像您想要的那样以“或”方式限制类列表。 (事实上​​,我什至不确定它在 IL 中是否直接合法。)

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

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

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

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

namespace TestServices
{
   internal interface ICommon
   {
   }

   internal partial class Service1SoapClient : ICommonInterface
   {
   }
}

【讨论】:

  • 很公平,但我希望你被证明是错的!关于这是为什么的任何扩展?我不拥有代码 - 是的,我可以通过 Reference.cs 并手动将每个类分配给一个公共接口,但如果队友更新了 Web 引用 - BAM!没有更多界面了。
  • 查看 Jon Skeet 关于使用部分类从通用接口派生所有内容的可能性的回答。
  • 请参阅这篇文章,了解为什么没有实现“切换类型”功能,这可能有许多相同的原因。 blogs.msdn.com/peterhal/archive/2005/07/05/435760.aspx
【解决方案3】:

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

使用反射还可以保证您希望确保对象实现您的新接口对象实现的任何属性,否则当您期望的属性未实现时,您可能会引发错误。

【讨论】:

    【解决方案4】:

    如果您从 Web 服务中提取这些对象,您绝对可以控制所使用的类定义。它们不会凭空出现(即使您最初有代码生成器或视觉工作室创建它们)。仍然有一个必须与应用程序一起编译的每个地方的类文件,您应该能够将通用接口添加到这些类定义中。

    【讨论】:

    • 源代码管理不就是为了这个吗?
    • 没错,我只是不喜欢这样做。代码味道?
    • 即使使用源代码控制,每次您对 reference.cs 进行更改时,它都会重新提交。仍然必须有人去手动恢复到“接口”副本......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    • 1970-01-01
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多