简体   繁体   中英

How to have a public class inherit from private interface, without getting Inconsistent accessibility error

Here's a paraphrasing of what I want to do:

public class MyClass
{
  public T GetFoo<T>() : where T : class, MyInterface
  {
    if (typeof(T) == typeof(Class1)
    {
      return new Class1() as T;
    }
    else if (typeof(T) == typeof(Class2))
    {
      return new Class2() as T;
    }
    else
    {
      return default(T);
    }
  }

  private interface MyInterface {}  // This is actually empty, it doesn't do anything except limit the types that can be passed to GetFoo()

  public class Class1 : MyInterface
  {
    // Stuff
  }

  public class Class2 : MyInterface
  {
    // Other Stuff
  }
  // there are many more such classes that all inherit from MyInterface
}

So, I've got a public class with a public method. The method accepts a generic type parameter. But I want to limit the type of T that it accepts, so that's why it's using the MyInterface. Of course that fails to compile because MyInterface is private. It throws an "Inconsistent accessibility: constraint type is less accessible than " error.

But here's why I want it to work this way:

Each of the Class1, Class2, etc are declared public, so that others can use them. But I want to restrict others from being able to declare their own such classes and pass them to the GetFoo() method. Because that will break GetFoo(), so that's why I want MyInterface to be private.

If I make MyInterface public, of course it will compile and everything will work just fine. But I need to be able to prevent others from declaring their own classes and inheriting MyInterface and passing that to GetFoo().

I want to allow callers to do this:

Class1 userFoo = GetFoo<Class1>();

I want to PREVENT callers from doing this:

Class UserClass : MyInterface {}
...
UserClass userFoo = GetFoo<UserClass>();

Edit: Thanks for all the very fast replies. Yes I know that's not the purpose of Interface's, it just seemed to make sense to me at the time. I am certainly open to a more elegant solution if one exists.

You cannot. This is fundamentally impossible. By making the interface private , the caller effectively does not know whether a class implements that interface. But by using it in a type constraint, you do require the caller to know whether a class implements that interface.


What you could do is use a common base class: even though that class has to be public , you can prevent others from deriving from it.

public class Base {
  internal Base() { }
}
public interface IBase { }
public sealed class Class1 : Base, IBase {
  ...
}
public sealed class Class2 : Base, IBase {
  ...
}
public T GetFoo<T>() where T : Base, IBase {
  ...
}

The IBase interface is to make sure that GetFoo<Base> will be rejected. Class1 and Class2 are sealed to prevent others from deriving from those.

However, this approach can only work if Class1 and Class2 are supposed to have a common base class.


I encourage to re-think your design, though. Almost always, if your generic code has a bunch of typeof(T) == typeof(SomeConcreteClass) conditionals, that's a strong indication that you'd have been better off creating separate methods for each concrete type. But there are exceptions, and I won't rule out the possibility of your code being one of the exceptions.

It makes no sense at all to create a class that implements an interface and to try and hide the abstraction that the interface is offering to you.

OO is not about programming towards objects, what you are definitely trying to do, but about programming towards interfaces, and you are trying to hide them.

Better think of a different approach.

This code works and compiles:

public class MyClass<T> where T : class, IMyInterface
{
    public T GetFoo()
    {
        if (typeof (T) == typeof (Class1))
        {
            return new Class1() as T;
        }
        else if (typeof (T) == typeof (Class2))
        {
            return new Class2() as T;
        }
        else
        {
            return default(T);
        }
    }
}

public interface IMyInterface {}  // This is actually empty, it doesn't do anything except limit the types that can be passed to GetFoo()

public class Class1 : IMyInterface
{
// Stuff
}

public class Class2 : IMyInterface
{
// Other Stuff
}
// there are many more such classes that all inherit from MyInterface

And Main method which works fine:

    static void Main(string[] args)
    {
        var a1 = new MyClass<Class1>();
        var a2 = a1.GetFoo();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM