internal member in an interface
Asked Answered
C

13

50

I have a list of objects implementing an interface, and a list of that interface:

public interface IAM
{
    int ID { get; set; }
    void Save();
}

public class concreteIAM : IAM
{
     public int ID { get; set; }
     internal void Save(){
     //save the object
     }

    //other staff for this particular class
}

public class MyList : List<IAM>
{
    public void Save()
    {
        foreach (IAM iam in this)
        {
            iam.Save();
        }
    }

    //other staff for this particular class
}

The previous code doesn't compile because the compiler requires all the interface members to be public.

internal void Save(){

But i don't want to allow the from outside my DLL to save the ConcreteIAM, it only should be saved through the MyList.

Any way to do this?

Update#1: Hi all, thanks for the answers so far, but none of them is exactly what i need:

The interface needs to be public because it is the signature the client from outside the dll will use, along with ID and other properties i didn't bother to write in the example to keep it simple.

Andrew, I don't think the solution is create a factory to create another object that will contain the IAM members + Save. I am still thinking... Any other ideas?

Censurable answered 15/12, 2008 at 4:13 Comment(3)
thanks for the answers so far, but none of them is exactly what i need: The interface needs to be public because it is the signature the client from outside the dll will use, along with ID and other properties i didn't bother to write in the example to keep it simple. Andrew, I don't think the solution is create a factory to create another object that will contain the IAM members + Save. I am still thinking... Any other ideas?Censurable
If you want to keep the Save method on the concreteAM class your best bet is probably to go with an abstract base class with an internal abstract Save method as suggested by Nathan W.Flowers
better late than never but this may help: https://mcmap.net/q/355395/-how-to-create-an-interface-that-keeps-some-methods-internal-for-testing-in-cRolypoly
P
54

I think you don't understand what an interface is for. Interface is a contract. It specifies that an object behaves in a certain way. If an object implements an interface, it means that you can rely on it that it has all the interface's methods implemented.

Now, consider what would happen if there was an interface like you're asking for - public, but with one internal member. What would that mean? An external object could implement only the public methods, but not the internal one. When you would get such an external object, you would be able to call only the public methods, but not the internal one, because the object couldn't implement it. In other words - the contract would not be fulfilled. Not all of the methods would be implemented.

I think that the solution in this case is to split your interface in two. One interface would be public, and that's what your external objects would implement. The other interface would be internal and would contain your Save() and other internal methods. Perhaps this second interface could even inherit from the first. Your own internal objects would then implement both interfaces. This way you could even distinguish between external objects (ones that don't have the internal interface) and internal objects.

Patchy answered 15/12, 2008 at 9:55 Comment(13)
Simple scenario: an assembly has classes Thing1 and Thing2 which for various reasons cannot usefully share a base type, but do share an interface IThing. It wishes to give outside code objects which can be passed back to code that needs an IThing, but it wants to guarantee that any IThing it receives will actually implement the IThing contract properly, by forbidding any implementations from outside the assembly. One could achieve a similar effect by using a wrapper class or struct, but code would be cleaner if one didn't have to do that.Oshaughnessy
I'm afraid I can't think of a way to achieve this in C#. However, there are many things that cannot be achieved in C# (or in any other language, for that matter). That's why code contracts exist.Patchy
I know it's been 5 years, but I feel the need to respond. What he's asking for is equivalent to 'internal protected abstract'. It suffers from all the same side effects (none). The implementer would be forced to implement it. It would be callable only from the assembly that owned the method definition and from the implementer itself. Same thing, exactly. Just, not supported.Hamlett
@wasabi - An interface is a list of public methods that a class must have. That's its purpose. It doesn't care (nor ever will care, even in future versions of .NET) about private/protected/internal methods.Patchy
@Patchy I'm quite aware of what an interface IS. What I'm saying is that what the OP is asking for doesn't have side effects. It might not exist in the language, and I think we all know that, but it COULD exist in the language. And I'm not suggesting it will. But it could.Hamlett
@wasabi - So... it would be an interface that is publicly visible, but has members that are internal, so it can only be implemented internally? I'm not sure what the implications of that would be. It "feels" somehow that this would cause serious conflicts in the CLR, but I don't know enough to justify this gut feeling. It would be interesting to hear the opinion of someone knowledgeable on the subject.Patchy
Only from the use-case point of view makes sense to define a contract that specifies that a method will exist but will be only callable from objects stored in the same assembly. I would like to have feature, certainly.Pisa
I have the same problem some years on, I have an engine that requires Models to provide an internal Render() method, not a public method, an internal method, because the game code is not responsible for drawing. And yet, interfaces don't provide internal contracts. That's a terrible shortcoming. There is absolutely no reason, a contract provided by the engine can't say - 'you must provide an internal drawing method, because i require it, and i understand that you have custom drawing code. And I'm a game engine using an entity component system and i don't like to have too much inheritance'Adorable
@Vilx-: "an interface that is publicly visible, but has members that are internal, so it can only be implemented internally?" - exactly. It's the same as a non-sealed class with an internal constructor: Visible, but it can only be subclassed internally. I regularly employ that pattern when I want to provide API users with an object hierarchy that they cannot modify from the outside (or only at defined "open-ended" nodes rather than right at the base class). I have repeatedly wished for doing something similar with interfaces.Aquitaine
@O.R.Mapper - So, basically, you want an interface that can be passed to external code, but can only be implemented by internal classes. Well... I can see the use for this. Unfortunately I'm not enough well-versed in the internals of .NET and it's design decisions to say why it isn't currently possible. I suspect there is a reason though.Patchy
@Vilx-: Yes, that's exactly what I want. I'm not sure there is a technical reason, given that the same can be done with internal constructors, or with internal abstract methods, as described above. I suppose it's simply a case of deciding against additional complexity in the C# compiler (or even the CLI runtime definition of interfaces).Aquitaine
@O.R.Mapper - Some guesses: there could be compatibility issues with COM (.NET is VERY friendly to COM and a lot of .NET concepts map directly to COM concepts) or other languages that they wanted easy interoperability with; interfaces and classes could be very different beasts from the low-level perspective (MSIL) and introducing modifiers could really complicate things there; there could also be ideological issues (like if you explicitly define an interface as a "public contract" or something then internal members go against that).Patchy
@O.R.Mapper - And last but not least - every feature starts with -100 points. There is only so much time available for development, and the wishlist is always much longer than what can be realistically accomplished. So you need to pick a few things which are most useful. Perhaps this feature simply didn't make the cut. Heck, extension properties are even more widely demanded, and even they haven't gotten the green light yet (AFAIK).Patchy
B
43

Make another interface that is internal, and use explicit implementation for the method.

internal interface InternalIAM
{
    void Save();
}

public class concreteIAM : InternalIAM
{
    void InternalIAM.Save()
    {
    }
}
Bergess answered 15/12, 2008 at 10:23 Comment(3)
Thanks, always wanted to know how to have a public class implement an internal interface.Smoulder
A problem with this is that you cannot have public methods that use InternalIAM as a parameter type.Aquitaine
Not perfect but the best compromise so far.Itacolumite
C
9

I think the best thing to do would be to break the internal and public members into two separate interfaces. If you inherit the interfaces you can still declare the members publicly but the visibility of the internal members will be dictated by the visibility of the interface itself.

using System;

public interface IPublic
{
    void Public();
}

internal interface IInternal : IPublic
{
    void Internal();
}

public class Concrete : IInternal
{
    public void Internal() { }

    public void Public() { }
}
Contour answered 15/12, 2008 at 14:15 Comment(4)
the visibility of the members is dictated by the implmentation of the member. If you instantiate an object Concrete you can call its Public method.Censurable
that is a good point - I imagined some sort of factory method that would return Concrete as IPublic thus hiding all other membersContour
Could also be combined with explicit implementation then but it has its pros and cons.Itacolumite
This works great with Dependency Injection in .NET Core.Wayzgoose
L
8

I don't think you should be using an interface here. Maybe you should be using an abstract base, something like:

public abstract class AM
{
    public int ID { get; set; }
    internal abstract void Save();
}

public class concreteIAM : AM
{
    internal override void Save()
    {
        //Do some save stuff
    }
}

Will still allow you to do this:

public class AMList : List<AM>
{
    public void SaveItems()
    {
        foreach (var item in this)
        {
            item.Save();
        }
    }
}
Latinist answered 15/12, 2008 at 4:18 Comment(1)
Quite limiting since we don't have multiple inheritance unfortunately.Itacolumite
H
4

It is exactly what I am talking about in my article Friends and internal interface members at no cost with coding to interfaces.

The essence from the article

public class Internal
{
    internal Internal() { }
}

public interface IAM
{
    int ID { get; set; }
    void Save(Internal access);
}

From now on, only your assembly can call the Save() method, since an instance of the Internal class can only be created by your assembly.

Hairbreadth answered 21/6, 2012 at 19:35 Comment(1)
Unless they somehow get a hold of the object, no? It only prevents them from instanciating it, not accessing its public field if they got access to the object itself. Additionally, I assume you meant Internal to inheri from IAM and implement ID and Save.Itacolumite
L
1

If you don't want external callers to be able to call your Save() method, why not make the whole concreteIAM class internal?

Or, if you want the class public, but not the interface, make the whole interface internal. I think an internal interface can be added to a public class (but I haven't tried it...)

Layoff answered 15/12, 2008 at 5:1 Comment(0)
F
1

Maybe you want to separate the saving of your items into a different set of classes that are internal to your assembly:

internal interface IAMSaver { void Save(IAM item); }

internal class AMSaverFactory {
  IAMSaver GetSaver(Type itemType) { ... }
}

public class MyList : List<IAM>
{
  public void Save()
  {
    foreach (IAM itemin this)
    {
      IAMSaver saver = SaverFactory.GetSaver(item.GetType());
      saver.Save(item)
    }
  }
}
Flowers answered 15/12, 2008 at 5:16 Comment(0)
R
1

Interface members are supposed to be public.. anything else and you should be thinking if you need something else. In this case, you

  • want Save to be an interface member
  • want Save to be only allowed via another class called MyList which lies in the same assembly
  • disallow Save from being called externally (from other classes outside the parent assembly)

Keeping design thoughts aside, you could do this via Explicit Interface Implementation.

    internal interface IPersist
    {
        void Save();
    }
    public class Concrete : IPersist
    {
        void IPersist.Save()
        {
            Console.WriteLine("Yeah!");
        }
    }

// Mylist.cs in the same assembly can still call save like
public void SaveItems()
    {
        foreach (IPersist item in this)
        {
            item.Save();
        }
    }

IPersist is internal, not available outside the parent assembly and since it is explicitly implemented, it cannot be called without an IPersist reference.

new Concrete().Save();     // doesn't compile. 'Concrete' does not contain a definition for 'Save'

Update From your latest response, we have more constraints. The Interface must be public, it should contain the Save method, which shouldn't be publicly available. I'd say go back to the drawing board... something doesn't seem right. IMHO You can't do this with a single interface. How about splitting into 2 interfaces, the public one and the internal one?

Raseta answered 15/12, 2008 at 5:31 Comment(0)
S
1

Go with two interfaces:

public interface IAM
{
        int ID { get; set; }
}


internal interface IAMSavable
{
        void Save();
}

public class concreteIAM : IAM, IAMSavable
{
         public int ID{get;set;}
         public void IAMSavable.Save(){
         //save the object
         }

        //other staff for this particular class
}

public class MyList : List<IAM>
{
        public void Save()
        {
                foreach (IAM iam in this)
                {
                        ((IAMSavable)iam).Save();
                }
        }

        //other staff for this particular class
}

The implementation of Save() must be explicit to prevent clients calling it.

Santinasantini answered 15/12, 2008 at 10:4 Comment(0)
P
1

Why don't you use inner classes to control accessibility of your methods?

Example:

Your primary assembly

public abstract class Item
{
    public int ID { get; set; }
    protected abstract void Save();

    public class ItemCollection : List<Item>
    {
        public void Save()
        {
            foreach (Item item in this) item.Save();
        }
    }
}

Your secondary assembly

public sealed class NiceItem : Item
{
    protected override void Save()
    {
        // do something
    }
}

This pattern will still let you implement Save() method in other assemblies but only ItemCollection which is inner class of Item can call it. Brilliant, isn't it?

Poulos answered 15/12, 2008 at 10:7 Comment(0)
P
1

What about this?

public interface IAM
{
    int ID { get; set; }
    internal void Save();
}

public class concreteIAM : IAM
{
     public int ID { get; set; }
     void IAMSavable.Save() {
         //save the object
     }
}

You can declare a member of an interface as internal, however when you implement it in a class you only have two options: either you declare it as public or as an explicit interface implementation. By implementing it as an explicit interface implementation, as done in the example code, Save() is only accessible by casting concreteIAM to IAM, thus Save() becomes is effectively internal.

This is conceptually very similar to the solution proposed in this answer, however there is no need to declare a second interface.

Plantain answered 28/6, 2021 at 17:15 Comment(0)
B
0

I had similar situation and thought this could help. (not sure if this is not needed at this time or not)

This is a legit case for me: in case suppose there is an interface which has some API which is internal to the DLL and some API can be accessed from outside the DLL. Since interface is an abstract class, instead of defining as an interface you can define as a abstract class.

using System.Collections.Generic;

public abstract class IAM
{
    int ID { get; set; }
    internal abstract void Save();
}

public class concreteIAM : IAM
{
    public int ID { get; set; }
    internal override void Save()
    {
        //save the object
    }

    //other staff for this particular class
}

public class MyList : List<IAM>
{
     internal void Save()
    {
        foreach (IAM iam in this)
        {
            iam.Save();
        }
    }

    //other staff for this particular class
}
Burroughs answered 22/10, 2018 at 17:24 Comment(0)
T
-2

I was wondering on the same issue here, and stumbled upon this question...

As i thought on this, i understood i don't need the internal method in the Interface in the first place.

I can access it through my Concrete Class, and leave the Contract for the Out-Side code.

In your example:

    public interface IAM
    {
            int ID { get; set; }
    }

    public class concreteIAM : IAM
    {
             public int ID{get;set;}
             internal void Save(){
             //save the object
             }

            //other staff for this particular class
    }

    public class MyList : List<IAM>
    {
            public void Save()
            {
                    foreach (concreteIAM iam in this)
                    {
                            iam.Save();
                    }
            }
            //other staff for this particular class
    }
Topographer answered 11/1, 2012 at 17:12 Comment(3)
-1, results in a complication error.'concreteIAM' does not implement interface member 'IAM.Save()'Introspect
Whenever someone has to say 'hope this helps', it usually doesn't. Such as here.Like
as by popular demand, fixed the example... the interface should have never define Save in the first placeTopographer

© 2022 - 2024 — McMap. All rights reserved.