【问题标题】:C# generic typing with multiple class constraints具有多个类约束的 C# 泛型类型
【发布时间】:2018-06-05 22:33:15
【问题描述】:

TL;DR

我想用 C# 编译它

public void some_method< T >() where T : class1, class2

这可能吗?


完整上下文

除了一个参数外,我有两种相同的方法。

public SignInResponseMessage Generate(SignInRequestMessage request, (X509Certificate2 || WindowsPrincipal) principal, Uri requestUri)
{
    SignInResponseMessage response = null;
    ClaimsIdentity identity = null;

    if (principal != null)
    {
        identity = CreateSubject(principal);
        response = Generate(request, requestUri, identity);
    }
    else
    {
        throw new ArgumentNullException("principal");
    }

    return response;
}

我目前正在复制这种方法,它让我内心有些畏缩,因为我真的很想做这个DRY-er。环顾四周,this documentation 似乎很有希望,但它只允许我添加一个类约束。我在第二堂课上收到以下错误:

错误 1 ​​类类型约束“class2”必须位于任何其他约束之前

如果 WindowsPrincipalX509Certificate2 是我编写的两个类,我可以轻松地让它们实现相同的接口,我会很高兴,但这不是一个选择。

有什么方法可以完成我想做的事吗?

如果没有,我想了解更多关于使这成为不可能的底层机制

【问题讨论】:

  • 这是不可能的。一个潜在的解决方案是使用适配器模式,您的适配器实现您的接口,然后使用您的接口作为约束。
  • 你有适配器模式的好资源吗?这对我来说是一个新的
  • 四人帮已经很好地描述了它。您可以使用以下资源:blackwasp.co.uk/Adapter.aspx
  • C# 不允许多重继承,所以不能使用两个类作为类型约束。
  • 您可以使用where T : class1, interface2where T : class, interface1, interface2。然后通过巧妙地设置你的界面,你可以接近你想要的。

标签: c#


【解决方案1】:

我担心这可能会导致无法确定实际调用的方法。想象一下,如果指定的一个类继承自另一个类并且该方法有一个覆盖!?

Please see the "Diamond Problem" for a complete description of the reasons

如果你想解决它。您可以使用适配器设置公共共享接口,然后使用它。

interface IAdaper {
    SomeMethod();
}

class AdapterOne : IAdapter {
    TypeOneToCall  _one;

    public AdapterOne (TypeOneToCall one) {
        _one = one;
    }

    public SomeMethod() {
        return _one.SomeMethod();
    }
}

class AdapterTwo : IAdapter {
    TypeTwoToCall _two;

    public AdapterTwo (TypeTwoToCall two) {
        _two = two;
    }

    public SomeMethod() {
        return _two.SomeMethod();
    }
}

class Generic<T> where T : IAdapter {

    // Your implementation here.
}

【讨论】:

  • 调用方法是有意义的,但是是什么阻止了这样的功能在这个特定的用例中存在 - 其中一个对象被传递给一个实际上做不同事情的重载方法? IE。编译器是否有理由不能仅验证 T 是正确的类型(并且不允许在该方法中对 T 调用任何方法)?
  • 创建抽象的建议 (IAdapter) 是个好主意。在这种情况下,您甚至不需要 Generic 类是通用的,因为它不需要知道 IAdapter 背后的内容。
  • C# 的设计者都注重一致性,我个人倾向于同意他们的观点。如果它不能在所有情况下都很好地工作,它往往会被排除在外。
  • @Matt,方法调用看起来像public SignInResponseMessage Generate(SignInRequestMessage request, IAdapter principal, Uri requestUri),而不是泛型方法。
  • 我已经将这种模式用于各种应用程序。在 DI 中处理静态也是类似的事情。这与策略模式非常相关。 IAdapter 是一种策略。
【解决方案2】:

如果你将方法作为参数传递,那么 T 可以是任何东西:

  public SignInResponseMessage Generate<T>(SignInRequestMessage request, 
                                          Func<T, ClaimsIdentity> createSubject, 
                                          T principal, 
                                          Uri requestUri)
  {
      SignInResponseMessage response = null;
      ClaimsIdentity identity = null;

      if (principal != null)
      {
          identity = createSubject(principal);
          response = Generate(request, requestUri, identity);
      }
      else
      {
          throw new ArgumentNullException("principal");
      }

      return response;
  }

所以要重用方法:

  var r1 = Generate<X509Certificate2>(request, CreateSubject, certificate, uri);
  var r2 = Generate<WindowsPrincipal>(request, CreateSubject, principal, uri);

【讨论】:

  • 这绝对是更流畅且有效的,但我倾向于避免使用委托,因为它们会使代码阅读起来更加混乱
  • 这是一种非常实用的方法。做得对,它可以像任何 OO 模式一样干净和可读。加上更少的代码。不要把它写得太容易。
  • 我不同意过去的自己;这是一个很好的方法。
猜你喜欢
  • 1970-01-01
  • 2015-04-26
  • 1970-01-01
  • 1970-01-01
  • 2018-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多