Интерфейсы C #. Неявная реализация против явной реализации

Вот мой подход к открытию изображения без блокировки файла ...

public static Image FromFile(string path)
{
    var bytes = File.ReadAllBytes(path);
    var ms = new MemoryStream(bytes);
    var img = Image.FromStream(ms);
    return img;
}

UPDATE: Я сделал несколько перфекционных тестов, чтобы узнать, какой метод был самым быстрым. Я сравнил его с ответом @net_progs «копировать из растрового изображения» (который, кажется, ближе всего подходит, хотя и имеет некоторые проблемы). Я загрузил изображение 10000 раз для каждого метода и вычислил среднее время на изображение. Вот результаты:

Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.

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

607
задан Guillermo Gutiérrez 2 September 2014 в 23:03
поделиться

7 ответов

Неявный , когда Вы определяете свой интерфейс через участника на Вашем классе. Явный , когда Вы определяете методы в своем классе в интерфейсе. Я знаю, что сбивающие с толку звуки, но вот - то, что я имею в виду: IList.CopyTo был бы неявно реализован как:

public void CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

и явно как:

void ICollection.CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

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

MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.

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

я уверен, что существует больше причин использовать его, используют его, что другие отправят.

Посмотрите следующее сообщение в этом потоке для превосходного обоснования позади каждого.

466
ответ дан Community 2 September 2014 в 23:03
поделиться

Неявное определение должно было бы просто добавить методы / свойства, и т.д. потребованные интерфейсом непосредственно к классу как открытые методы.

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

  1. Путем работы непосредственно с интерфейсом, Вы не подтверждаете и связываете свой код с конкретной реализацией.
  2. , Если у Вас уже есть, скажем, Имя общественной собственности в Вашем коде и Вы хотите реализовать интерфейс, который также имеет свойство Name, делание его явно разделит два. Даже если бы они делали то же самое, то я все еще делегировал бы явный вызов к свойству Name. Вы никогда не знаете, можно хотеть измениться, как Имя работает на нормальный класс и как Имя, интерфейсное свойство работает позже.
  3. при реализации интерфейса неявно тогда класс теперь представляет новые поведения, которые могли бы только относиться к клиенту интерфейса, и это означает, что Вы не сохраняете свои классы достаточно сжатыми (мое мнение).
189
ответ дан David Pine 2 September 2014 в 23:03
поделиться

Если Вы реализуете явно, Вы только будете в состоянии сослаться на интерфейсных участников через ссылку, которая имеет тип интерфейса. Ссылка, которая является типом класса с реализацией, не представит тех интерфейсных участников.

, Если Ваш класс с реализацией не общедоступен, за исключением метода, используемого для создания класса (который мог быть фабрикой или МОК контейнер), и за исключением методов интерфейса (конечно), тогда я не вижу преимущества для явной реализации интерфейсов.

Иначе, явно реализация интерфейсов удостоверяется, что ссылки на Ваш конкретный класс с реализацией не используются, позволяя Вам изменить ту реализацию в более позднее время. "Удостоверяется", я предполагаю, "преимущество". Хорошо учтенная реализация может выполнить это без явной реализации.

недостаток, по-моему, то, что Вы бросите типы к/от интерфейсу в коде реализации, который действительно имеет доступ к непубличным участникам.

Как много вещей, преимуществом является недостаток (и наоборот). Явно реализующие интерфейсы гарантируют, что Ваш код реализации реального класса не представлен.

8
ответ дан Peter Mortensen 2 September 2014 в 23:03
поделиться

В дополнение к превосходным ответам уже, если, существуют некоторые случаи, где явная реализация ТРЕБУЕТСЯ для компилятора быть в состоянии выяснить то, что требуется. Смотрите на IEnumerable<T> как главный пример, который будет, вероятно, подходить справедливо часто.

Вот пример:

public abstract class StringList : IEnumerable<string>
{
    private string[] _list = new string[] {"foo", "bar", "baz"};

    // ...

    #region IEnumerable<string> Members
    public IEnumerator<string> GetEnumerator()
    {
        foreach (string s in _list)
        { yield return s; }
    }
    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
    #endregion
}

Здесь, IEnumerable<string> реализации IEnumerable, следовательно мы должны также. Но держитесь, и дженерик и нормальная версия обе функции реализации с той же сигнатурой метода (C# игнорирует тип возврата для этого). Это абсолютно законно и прекрасно. Как компилятор разрешает, чтобы использовать? Это вынуждает Вас только иметь, самое большее, одно неявное определение, тогда это может разрешить то, что это должно.

т.е.

StringList sl = new StringList();

// uses the implicit definition.
IEnumerator<string> enumerableString = sl.GetEnumerator();
// same as above, only a little more explicit.
IEnumerator<string> enumerableString2 = ((IEnumerable<string>)sl).GetEnumerator();
// returns the same as above, but via the explicit definition
IEnumerator enumerableStuff = ((IEnumerable)sl).GetEnumerator();

пз: маленький кусочек косвенности в явном определении для IEnumerable работает, потому что в функции компилятор знает, что фактическим типом переменной является StringList, и это - то, как это разрешает вызов функции. Изящный небольшой факт для реализации некоторых слоев абстракции некоторые интерфейсы ядра.NET, кажется, накопился.

65
ответ дан Benjamin 2 September 2014 в 23:03
поделиться

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

/// <summary>
/// This is a Book
/// </summary>
interface IBook
{
    string Title { get; }
    string ISBN { get; }
}

/// <summary>
/// This is a Person
/// </summary>
interface IPerson
{
    string Title { get; }
    string Forename { get; }
    string Surname { get; }
}

/// <summary>
/// This is some freaky book-person.
/// </summary>
class Class1 : IBook, IPerson
{
    /// <summary>
    /// This method is shared by both Book and Person
    /// </summary>
    public string Title
    {
        get
        {
            string personTitle = "Mr";
            string bookTitle = "The Hitchhikers Guide to the Galaxy";

            // What do we do here?
            return null;
        }
    }

    #region IPerson Members

    public string Forename
    {
        get { return "Lee"; }
    }

    public string Surname
    {
        get { return "Oades"; }
    }

    #endregion

    #region IBook Members

    public string ISBN
    {
        get { return "1-904048-46-3"; }
    }

    #endregion
}

Этот код компиляции и выполнения хорошо, но свойство Title совместно используется.

Очевидно, мы хотели бы значение Заголовка, возвращенного для зависимости от того, рассматривали ли мы Class1 как Книгу или Человека. Это - когда мы можем использовать явный интерфейс.

string IBook.Title
{
    get
    {
        return "The Hitchhikers Guide to the Galaxy";
    }
}

string IPerson.Title
{
    get
    {
        return "Mr";
    }
}

public string Title
{
    get { return "Still shared"; }
}

Уведомление, что явные интерфейсные определения выведены для Общественности - и следовательно Вы не можете объявить, что они общедоступны (или иначе) явно.

Примечание также, что у Вас может все еще быть "общая" версия (как показано выше), но пока это возможно, существование такого свойства сомнительно. Возможно, это могло использоваться в качестве реализации по умолчанию Заголовка - так, чтобы существующий код не должен был быть изменен для кастинга Class1 к IBook или IPerson.

, Если Вы не определяете "общий" (неявный) Заголовок, потребители Class1 должны явно экземпляры броска Class1 к IBook или IPerson сначала - иначе, код не скомпилирует.

18
ответ дан Peter Mortensen 2 September 2014 в 23:03
поделиться

Предыдущие ответы объясняют, почему реализация интерфейса явно в C# может быть предпочтительна (по главным образом формальным причинам). Однако существует одна ситуация, где явная реализация обязательна : Чтобы постараться не пропускать инкапсуляцию, когда интерфейс не - public, но класс с реализацией public.

// Given:
internal interface I { void M(); }

// Then explicit implementation correctly observes encapsulation of I:
// Both ((I)CExplicit).M and CExplicit.M are accessible only internally.
public class CExplicit: I { void I.M() { } }

// However, implicit implementation breaks encapsulation of I, because
// ((I)CImplicit).M is only accessible internally, while CImplicit.M is accessible publicly.
public class CImplicit: I { public void M() { } }

вышеупомянутая утечка неизбежна, потому что, согласно спецификация C#, "У всех интерфейсных участников неявно есть открытый доступ". Как следствие неявные реализации должны также дать public доступ, даже если сам интерфейс, например, internal.

реализация интерфейса Implicit в C# является большим удобством. На практике многие программисты используют его все время/везде без дальнейшего соображения. Это приводит к грязным поверхностям типа в лучшем случае и пропустило инкапсуляцию в худшем случае. Другие языки, такие как F#, даже не позволяют его .

1
ответ дан 22 November 2019 в 21:58
поделиться

Причина № 1

Я предпочитаю использовать явную реализацию интерфейса, когда хочу отговорить «программирование на реализацию» ( Принципы проектирования из шаблонов проектирования ).

Например, в веб-приложении на основе MVP :

public interface INavigator {
    void Redirect(string url);
}

public sealed class StandardNavigator : INavigator {
    void INavigator.Redirect(string url) {
        Response.Redirect(url);
    }
}

Теперь другой класс (например, презентатор ) с меньшей вероятностью будет зависеть от реализации StandardNavigator и более вероятно зависеть от интерфейса INavigator (поскольку реализация должна быть приведена к интерфейсу, чтобы использовать метод Redirect).

Причина №2

Другой причиной, по которой я мог бы пойти с явной реализацией интерфейса, было бы сохранение «чистоты» интерфейса класса «по умолчанию». Например, если бы я разрабатывал серверный элемент управления ASP.NET , мне могли бы понадобиться два интерфейса:

  1. Основной интерфейс класса, который используется разработчиками веб-страниц; и
  2. «Скрытый» интерфейс, используемый докладчиком, который я разрабатываю для обработки логики управления.

Ниже приводится простой пример. Это поле со списком, в котором перечислены клиенты. В этом примере разработчик веб-страницы не заинтересован в заполнении списка; вместо этого они просто хотят иметь возможность выбрать клиента по GUID или получить GUID выбранного клиента. Презентатор заполняет поле при загрузке первой страницы, и этот докладчик инкапсулируется элементом управления.

public sealed class CustomerComboBox : ComboBox, ICustomerComboBox {
    private readonly CustomerComboBoxPresenter presenter;

    public CustomerComboBox() {
        presenter = new CustomerComboBoxPresenter(this);
    }

    protected override void OnLoad() {
        if (!Page.IsPostBack) presenter.HandleFirstLoad();
    }

    // Primary interface used by web page developers
    public Guid ClientId {
        get { return new Guid(SelectedItem.Value); }
        set { SelectedItem.Value = value.ToString(); }
    }

    // "Hidden" interface used by presenter
    IEnumerable<CustomerDto> ICustomerComboBox.DataSource { set; }
}

Презентатор заполняет источник данных, и разработчику веб-страницы никогда не нужно знать о его существовании.

Но это не серебряное пушечное ядро ​​

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

32
ответ дан 22 November 2019 в 21:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: