внутренний член в интерфейсе - PullRequest
33 голосов
/ 15 декабря 2008

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

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

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

Ответы [ 12 ]

39 голосов
/ 15 декабря 2008

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

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

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

25 голосов
/ 15 декабря 2008

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

internal interface InternalIAM
{
    void Save();
}

public class concreteIAM : InternalIAM
{
    void InternalIAM.Save()
    {
    }
}
9 голосов
/ 15 декабря 2008

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

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();
        }
    }
}
7 голосов
/ 15 декабря 2008

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

using System;

public interface IPublic
{
    void Public();
}

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

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

    public void Public() { }
}
3 голосов
/ 21 июня 2012

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

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

public class Internal
{
    internal Internal() { }
}

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

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

1 голос
/ 15 декабря 2008

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

Пример:

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

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, может вызвать его. Бриллиант, не правда ли?

1 голос
/ 15 декабря 2008

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

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

Если оставить в стороне мысли о дизайне, вы можете сделать это с помощью явной реализации интерфейса.

    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 интерфейса: публичный и внутренний?

1 голос
/ 15 декабря 2008

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

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)
    }
  }
}
1 голос
/ 15 декабря 2008

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

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

0 голосов
/ 22 октября 2018

У меня была похожая ситуация, и я подумал, что это может помочь. (не уверен, если это не нужно в данный момент или нет)

Это законный случай для меня: в случае, если предположим, что есть интерфейс, который имеет некоторый API, который является внутренним для DLL, и к некоторому API можно получить доступ извне DLL. Поскольку интерфейс является абстрактным классом, вместо определения в качестве интерфейса вы можете определить его как абстрактный класс.

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