はじめに
前提としてC#のジェネリック制約ではTはIHogeを実装するが、IFugaは実装しないことのようなインターフェースの否定的な制約を記述をすることはできません。
public interface IHoge; public interface IFuga; public static class Sandbox { // IHogeを実装している型を引数として受け取れる // IFugaを実装しているかどうかは関係ない public static bool Execute<T>(T value) where T : IHoge { return true; } }
ただどうしてもコンパイルエラーで弾きたいなという場面がありまして、その方法を考えてみたのでメモを書き残しておきたいと思います。補足すると仮に適応できたとしてもハックに近いので推奨はしません。
やり方
まず[Obsolete]を活用して、コンパイルエラーにしたい組み合わせを記述します。
learn.microsoft.com
あとはinを活用してオーバーロードを定義すれば完成です。
learn.microsoft.com
public interface IHoge; public interface IFuga; public static class Sandbox { // Obsoleteのerrorという引数をtrueにしてコンパイルエラーになるようにする [Obsolete("TはIFugaを実装しないこと", true)] public static bool Execute<T>(T value) where T : IHoge, IFuga { return true; } // ジェネリック型制約のみ異なっても、C#ではオーバーロードを定義できない // inを付与するとオーバーロードをとして定義できる public static bool Execute<T>(in T value) where T : IHoge { return true; } }
このように記述することで、
IHoge・IFugaを実装していると前者を利用(コンパイルエラー)IHogeのみを実装していると後者を利用(コンパイルエラーにならない)
という挙動になります。

コンパイルエラーの回避方法
明示的にinを付与することでコンパイルエラーを回避することは可能です。
// 明示的にinを指定することでコンパイルエラーにならない var value = new A(); var x = Sandbox.Execute(in value);
まあこれは意図的でないとやらないと思うので、そこまで気にしなくても良いかもしれません。