Подтвердить что ты не робот

Внутренний член в интерфейсе

У меня есть список объектов, реализующих интерфейс, и список этого интерфейса:

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
}

Предыдущий код не компилируется, потому что компилятор требует, чтобы все члены интерфейса были общедоступными.

internal void Save(){

Но я не хочу позволять снаружи моей DLL сохранять ConcreteIAM, ее нужно сохранить только через MyList.

Как это сделать?

Обновление # 1: Привет всем, спасибо за ответы до сих пор, но ни один из них не является именно тем, что мне нужно:

Интерфейс должен быть общедоступным, потому что это подпись, которую клиент извне dll будет использовать, а также ID и другие свойства, которые я не удосужился написать в примере, чтобы это было просто.

Эндрю, я не думаю, что решение создает factory для создания другого объекта, который будет содержать членов IAM + Сохранить. Я все еще думаю... Любые другие идеи?

4b9b3361

Ответ 1

Я думаю, вы не понимаете, для чего нужен интерфейс. Интерфейс контракт. Он указывает, что объект ведет себя определенным образом. Если объект реализует интерфейс, это означает, что вы можете полагаться на него, чтобы он реализовал все методы интерфейса.

Теперь подумайте, что произойдет, если бы был интерфейс, о котором вы просите, - public, но с одним внутренним членом. Что это значит? Внешний объект может реализовывать только общедоступные методы, но не внутренние. Когда вы получите такой внешний объект, вы сможете вызывать только общедоступные методы, но не внутренние, потому что объект не может его реализовать. Другими словами - контракт не будет выполнен. Не все методы будут реализованы.

Я думаю, что решение в этом случае состоит в том, чтобы разделить ваш интерфейс на два. Один интерфейс будет общедоступным и что ваши внешние объекты будут реализованы. Другой интерфейс будет внутренним и будет содержать ваши Save() и другие внутренние методы. Возможно, этот второй интерфейс может даже наследовать от первого. Тогда ваши собственные внутренние объекты будут реализовывать оба интерфейса. Таким образом, вы можете даже различать внешние объекты (те, которые не имеют внутреннего интерфейса) и внутренние объекты.

Ответ 2

Сделайте другой интерфейс, который является внутренним, и используйте явную реализацию для этого метода.

internal interface InternalIAM
{
    void Save();
}

public class concreteIAM : InternalIAM
{
    void InternalIAM.Save()
    {
    }
}

Ответ 3

Я не думаю, что вы должны использовать интерфейс здесь, возможно, вы должны использовать абстрактную базу, например:

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

По-прежнему позволит вам сделать это:

public class AMList : List<AM>
{
    public void SaveItems()
    {
        foreach (var item in this)
        {
            item.Save();
        }
    }
}

Ответ 4

Я думаю, что лучше всего было бы разбить внутренние и публичные члены на два отдельных интерфейса. Если вы наследуете интерфейсы, вы все равно можете публиковать участников публично, но видимость внутренних членов будет определяться видимостью самого интерфейса.

using System;

public interface IPublic
{
    void Public();
}

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

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

    public void Public() { }
}

Ответ 5

Это именно то, о чем я говорю в своей статье Друзья и члены внутреннего интерфейса бесплатно при кодировании для интерфейсов.

Сущность статьи

public class Internal
{
    internal Internal() { }
}

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

Теперь только ваша сборка может вызывать метод Save(), так как экземпляр класса Internal может быть создан только вашей сборкой.

Ответ 6

Если вы не хотите, чтобы внешние вызывающие абоненты могли вызвать ваш метод Save(), почему бы не сделать весь класс specificIAM внутренним?

Или, если вы хотите, чтобы класс публичный, но не интерфейс, сделать весь интерфейс внутренним. Я думаю, что внутренний интерфейс может быть добавлен в открытый класс (но я его не пробовал...)

Ответ 7

Возможно, вы хотите разделить сохранение ваших предметов на другой набор классов, которые являются внутренними для вашей сборки:

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

Ответ 8

Члены интерфейса должны быть общедоступными.. что-нибудь еще, и вы должны думать, если вам нужно что-то еще. В этом случае вы

  • хотите Сохранить как член интерфейса
  • хотите сохранить только разрешенный доступ через другой класс MyList, который находится в той же сборке
  • disallow Сохранить от вызова извне (из других классов вне родительской сборки)

Сохраняя конструктивные мысли в стороне, вы можете сделать это через реализацию явного интерфейса.

    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 является внутренним, недоступным вне родительской сборки и, поскольку он явно реализован, он не может быть вызван без ссылки IPersist.

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

Обновить. Из вашего последнего ответа у нас больше ограничений. Интерфейс должен быть общедоступным, он должен содержать метод Save, который не должен быть общедоступным. Я бы сказал, вернитесь на чертежную доску... что-то не кажется правильным. ИМХО. Вы не можете сделать это с помощью одного интерфейса. Как насчет разделения на 2 интерфейса, общедоступный и внутренний?

Ответ 9

Почему бы вам не использовать внутренние классы для контроля доступности ваших методов?

Пример:

Ваша основная сборка

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

Ваша вторичная сборка

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

Этот шаблон по-прежнему позволит вам реализовать метод Save() в других сборках, но только ItemCollection, который является внутренним классом Item, может вызвать его. Блестящий, не так ли?

Ответ 10

Перейдите с двумя интерфейсами:

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
}

Реализация Save() должна быть явной, чтобы клиенты не вызывали ее.

Ответ 11

Мне было интересно по этой же проблеме, и наткнулся на этот вопрос...

Как я и думал об этом, я понял, что мне не нужен внутренний метод в интерфейсе.

Я могу получить доступ к нему через свой класс Concrete, и оставьте контракт для кода Out-Side.

В вашем примере:

    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
    }