views:

728

answers:

11

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 memebers 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 throught 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?

+3  A: 

I don't think you should be using a 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();
        }
    }
}
Nathan W
A: 

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...)

Brannon
A: 

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)
    }
  }
}
Andrew Kennan
A: 

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?

Gishu
A: 

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?

edi tyour original answer, don't answer your own question. This isn't a forum. people won't see these as responses. comment on their answers, and edit your question.
TheSoftwareJedi
+4  A: 

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.

Vilx-
Phew! someone actually stepping back and asking why, rather than trying to code around in circles...
Dan Vinton
A: 

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.

MZywitza
A: 

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?

lubos hasko
A: 

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()
    {
    }
}
Lasse V. Karlsen
A: 

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() { }
}
Andrew Hare
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.
that is a good point - I imagined some sort of factory method that would return Concrete as IPublic thus hiding all other members
Andrew Hare
A: 

Thanks again to all the ideas but we are still missing the poit: VS only allows to implement an interface member as public, even if the interface is internal. So even if i break the interface in two:

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

public class concreteIAM : IAM, IAMSave
{
    public int ID { get; set; }
    internal void Save()
    {
        //save the object                 
    }
    //other staff for this particular class        
}

I still get the compilation error:

'LMIG.DataObjects.DataModules.DataTransformation.concreteIAM' does not implement interface member 'LMIG.DataObjects.DataModules.DataTransformation.IAMSave.Save()'. 'LMIG.DataObjects.DataModules.DataTransformation.concreteIAM.Save()' cannot implement an interface member because it is not public.

Even if i could do that, the list is composed of objects implementing IAM. To call the Save() from within the list i would need to cast the object to IAMSave:

public class MyList : List<IAM>
{
 public void Save() { 
  foreach (IAM iam in this) { 
   ((IAMSave)iam).Save(); 
  } 
 }
 //other staff for this particular class        
}

In this example, one of the objects in the list may not be implementing the IAMSave interface, and I would only find it at run time (yeah, i could check the type with reflection but that is way out of the way and would not work anyway...) For that matter i could cast the object in the list to whatever i want as long as it has a save, but it would be missing the point of the interface at all.

About the base class, i am weighting that, but i prefer to favor the interface...

So i am still in square 1. More ideas?