C# generic typing with multiple class constraints

2020-03-01 10:26发布

TL;DR

I'd like this to compile in C#

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

Is this possible?


Full Context

I have two methods that are identical except for one parameter.

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

I'm currently replicating this method and it's making me cringe a little inside as I would really like to make this DRY-er. Looking around, this documentation seemed promising, but it only allows me to add a single class constraint. I get the following error on the second class:

Error 1 The class type constraint 'class2' must come before any other constraints

If WindowsPrincipal and X509Certificate2 were two classes I had written I could easily make them implement the same interface and I would be good to go, but that's not an option.

Is there any way to accomplish what I'd like to do?

If not, I'd like to know more about the underlying mechanism that makes this impossible.

标签: c#
2条回答
够拽才男人
2楼-- · 2020-03-01 10:51

I am afraid that this could lead to issues with working out what method to actually call. Imagine if one of the classes specified inherited from the other and there was an override for that method!?

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

If you want to work around it. You can set up the common shared interface with an adapter and then use that.

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.
}
查看更多
兄弟一词,经得起流年.
3楼-- · 2020-03-01 10:53

If you pass the method as a parameter, then T can be anything:

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

So to reuse the method:

  var r1 = Generate<X509Certificate2>(request, CreateSubject, certificate, uri);
  var r2 = Generate<WindowsPrincipal>(request, CreateSubject, principal, uri);
查看更多
登录 后发表回答