Интерфейс, определяющий подпись конструктора?

Странно, что это первый раз, когда я столкнулся с этой проблемой, но:

Как вы определяете конструктор в интерфейсе С#?

Edit
Некоторым людям нужен пример (это проект свободного времени, так что да, это игра)

IDrawable
 + Обновление
 + Draw

Чтобы иметь возможность обновлять (проверять край экрана и т.д.) и рисовать, ему всегда понадобится GraphicsDeviceManager. Поэтому я хочу убедиться, что объект имеет ссылку на него. Это будет принадлежать конструктору.

Теперь, когда я записал это, я думаю, что я реализую здесь IObservable, а GraphicsDeviceManager должен взять IDrawable... Кажется, что я не получаю рамки XNA, или рамки не очень хорошо продумываются.

Edit
Кажется, есть некоторая путаница в моем определении конструктора в контексте интерфейса. Интерфейс действительно не может быть создан, поэтому конструктор не нуждается. То, что я хотел определить, было сигнатурой для конструктора. Точно так же, как интерфейс может определять подпись определенного метода, интерфейс может определять подпись конструктора.

+414
источник поделиться
16 ответов

Как уже было отмечено, вы не можете иметь конструкторы интерфейса. Но так как это очень высокий результат в Google примерно через 7 лет, я думал, что я буду чип здесь, в частности, чтобы показать, как вы можете использовать абстрактный базовый класс в тандеме с вашим существующим интерфейсом и, возможно, сократить объем рефакторинга необходимых в будущем для подобных ситуаций. Эта концепция уже упоминалась в некоторых комментариях, но я подумал, что было бы полезно показать, как это сделать на самом деле.

Итак, у вас есть свой основной интерфейс, который выглядит так:

public interface IDrawable
{
    void Update();
    void Draw();
}

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

public abstract class MustInitialize<T>
{
    public MustInitialize(T parameters)
    {

    }
}

Теперь вам нужно создать новый класс, который наследуется как от интерфейса IDrawable, так и от абстрактного класса MustInitialize:

public class Drawable : MustInitialize<GraphicsDeviceManager>, IDrawable
{
    GraphicsDeviceManager _graphicsDeviceManager;

    public Drawable(GraphicsDeviceManager graphicsDeviceManager)
        : base (graphicsDeviceManager)
    {
        _graphicsDeviceManager = graphicsDeviceManager;
    }

    public void Update()
    {
        //use _graphicsDeviceManager here to do whatever
    }

    public void Draw()
    {
        //use _graphicsDeviceManager here to do whatever
    }
}

Затем просто создайте экземпляр Drawable, и вы хорошо пойдете:

IDrawable drawableService = new Drawable(myGraphicsDeviceManager);

Приятно то, что новый класс Drawable, который мы создали, все еще ведет себя так же, как ожидалось от IDrawable.

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

+47
источник

Вы не можете. Иногда это боль, но в любом случае вы не сможете назвать ее обычными методами.

В сообщении в блоге я предложил статические интерфейсы, которые могли бы использоваться только в ограничениях типа общего типа, но могут быть очень удобными, ИМО.

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

public class Foo : IParameterlessConstructor
{
    public Foo() // As per the interface
    {
    }
}

public class Bar : Foo
{
    // Yikes! We now don't have a parameterless constructor...
    public Bar(int x)
    {
    }
}
+290
источник
другие ответы

Связанные вопросы


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

Очень поздний вклад, демонстрирующий еще одну проблему с сопряженными конструкторами. (Я выбираю этот вопрос, потому что он имеет четкое изложение проблемы). Предположим, что мы могли бы:

interface IPerson
{
    IPerson(string name);
}

interface ICustomer
{
    ICustomer(DateTime registrationDate);
}

class Person : IPerson, ICustomer
{
    Person(string name) { }
    Person(DateTime registrationDate) { }
}

Если по соглашению реализация "конструктора интерфейса" заменяется именем типа.

Теперь создайте экземпляр:

ICustomer a = new Person("Ernie");

Можно ли сказать, что контракт ICustomer выполняется?

А как насчет этого:

interface ICustomer
{
    ICustomer(string address);
}
+123
источник

Вы не можете.

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

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

+53
источник

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

public interface IFoo<T> where T : new()
{
  void SomeMethod();
}

public class Foo : IFoo<Foo>
{
  // This will not compile
  public Foo(int x)
  {

  }

  #region ITest<Test> Members

  public void SomeMethod()
  {
    throw new NotImplementedException();
  }

  #endregion
}

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

public static class TypeHelper
{
  public static bool HasParameterlessConstructor(Object o)
  {
    return HasParameterlessConstructor(o.GetType());
  }

  public static bool HasParameterlessConstructor(Type t)
  {
    // Usage: HasParameterlessConstructor(typeof(SomeType))
    return t.GetConstructor(new Type[0]) != null;
  }
}

Надеюсь, что это поможет.

+19
источник

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

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

public abstract class Foo
{
  protected Foo(SomeParameter x)
  {
    this.X = x;
  }

  public SomeParameter X { get; private set }
}

public class Bar : Foo // Bar inherits from Foo
{
  public Bar() 
    : base(new SomeParameter("etc...")) // Bar will need to supply the constructor param
  {
  }
}
+19
источник

Общий подход factory по-прежнему кажется идеальным. Вы знаете, что для параметра factory требуется параметр, и было бы так, что эти параметры передаются вместе с конструктором создаваемого объекта.

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

public interface IDrawableFactory
{
    TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager) 
              where TDrawable: class, IDrawable, new();
}

public class DrawableFactory : IDrawableFactory
{
    public TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager) 
                     where TDrawable : class, IDrawable, new()
    {
        return (TDrawable) Activator
                .CreateInstance(typeof(TDrawable), 
                                graphicsDeviceManager);
    }

}

public class Draw : IDrawable
{
 //stub
}

public class Update : IDrawable {
    private readonly GraphicsDeviceManager _graphicsDeviceManager;

    public Update() { throw new NotImplementedException(); }

    public Update(GraphicsDeviceManager graphicsDeviceManager)
    {
        _graphicsDeviceManager = graphicsDeviceManager;
    }
}

public interface IDrawable
{
    //stub
}
public class GraphicsDeviceManager
{
    //stub
}

Пример возможного использования:

    public void DoSomething()
    {
        var myUpdateObject = GetDrawingObject<Update>(new GraphicsDeviceManager());
        var myDrawObject = GetDrawingObject<Draw>(null);
    }

Конечно, вам нужно только создать экземпляры с помощью factory, чтобы гарантировать, что у вас всегда есть правильно инициализированный объект. Возможно, использование концепции инъекций зависимостей, например AutoFac, имеет смысл; Update() может "спросить" контейнер IoC для нового объекта GraphicsDeviceManager.

+4
источник

Один из способов решить эту проблему, которую я нашел, - это разделить конструкцию на отдельный factory. Например, у меня есть абстрактный класс с именем IQueueItem, и мне нужен способ перевести этот объект на другой объект (CloudQueueMessage) и обратно. Итак, на интерфейсе IQueueItem у меня есть -

public interface IQueueItem
{
    CloudQueueMessage ToMessage();
}

Теперь мне также нужно, чтобы мой фактический класс очереди переводил CloudQueueMessage обратно в объект IQueueItem, т.е. необходимость создания статической конструкции, такой как IQueueItem objMessage = ItemType.FromMessage. Вместо этого я определил другой интерфейс IQueueFactory -

public interface IQueueItemFactory<T> where T : IQueueItem
{
    T FromMessage(CloudQueueMessage objMessage);
}

Теперь я могу, наконец, написать свой общий класс очереди без ограничения new(), которое в моем случае было основной проблемой.

public class AzureQueue<T> where T : IQueueItem
{
    private IQueueItemFactory<T> _objFactory;
    public AzureQueue(IQueueItemFactory<T> objItemFactory)
    {
        _objFactory = objItemFactory;
    }


    public T GetNextItem(TimeSpan tsLease)
    {
        CloudQueueMessage objQueueMessage = _objQueue.GetMessage(tsLease);
        T objItem = _objFactory.FromMessage(objQueueMessage);
        return objItem;
    }
}

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

 AzureQueue<Job> objJobQueue = new JobQueue(new JobItemFactory())

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

+3
источник

Вы можете сделать это с помощью трюка generics, но он все еще уязвим для того, что написал Джон Скит:

public interface IHasDefaultConstructor<T> where T : IHasDefaultConstructor<T>, new()
{
}

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

public class A : IHasDefaultConstructor<A> //Notice A as generic parameter
{
    public A(int a) { } //compile time error
}
+2
источник

Одним из способов решения этой проблемы является использование дженериков и ограничение new().

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

Для примера с IDrawable:

public interface IDrawable
{
    void Update();
    void Draw();
}

public interface IDrawableConstructor<T> where T : IDrawable
{
    T Construct(GraphicsDeviceManager manager);
}


public class Triangle : IDrawable
{
    public GraphicsDeviceManager Manager { get; set; }
    public void Draw() { ... }
    public void Update() { ... }
    public Triangle(GraphicsDeviceManager manager)
    {
        Manager = manager;
    }
}


public TriangleConstructor : IDrawableConstructor<Triangle>
{
    public Triangle Construct(GraphicsDeviceManager manager)
    {
        return new Triangle(manager);
    } 
}

Теперь, когда вы его используете:

public void SomeMethod<TBuilder>(GraphicsDeviceManager manager)
  where TBuilder: IDrawableConstructor<Triangle>, new()
{
    // If we need to create a triangle
    Triangle triangle = new TBuilder().Construct(manager);

    // Do whatever with triangle
}

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

public DrawableConstructor : IDrawableConstructor<Triangle>,
                             IDrawableConstructor<Square>,
                             IDrawableConstructor<Circle>
{
    Triangle IDrawableConstructor<Triangle>.Construct(GraphicsDeviceManager manager)
    {
        return new Triangle(manager);
    } 

    Square IDrawableConstructor<Square>.Construct(GraphicsDeviceManager manager)
    {
        return new Square(manager);
    } 

    Circle IDrawableConstructor<Circle>.Construct(GraphicsDeviceManager manager)
    {
        return new Circle(manager);
    } 
}

Чтобы использовать его:

public void SomeMethod<TBuilder, TShape>(GraphicsDeviceManager manager)
  where TBuilder: IDrawableConstructor<TShape>, new()
{
    // If we need to create an arbitrary shape
    TShape shape = new TBuilder().Construct(manager);

    // Do whatever with the shape
}

Другой способ - использование лямбда-выражений в качестве инициализаторов. В какой-то момент в начале иерархии вызовов вы узнаете, какие объекты вам нужно создать (т.е. Когда вы создаете или получаете ссылку на объект GraphicsDeviceManager). Как только у вас есть это, пройдите лямбда

() => new Triangle(manager) 

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

+2
источник

нет.

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

0
источник

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

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

public interface IFoo {

    /// <summary>
    /// Initialize foo.
    /// </summary>
    /// <remarks>
    /// Classes that implement this interface must invoke this method from
    /// each of their constructors.
    /// </remarks>
    /// <exception cref="InvalidOperationException">
    /// Thrown when instance has already been initialized.
    /// </exception>
    void Initialize(int a);

}

public class ConcreteFoo : IFoo {

    private bool _init = false;

    public int b;

    // Obviously in this case a default value could be used for the
    // constructor argument; using overloads for purpose of example

    public ConcreteFoo() {
        Initialize(42);
    }

    public ConcreteFoo(int a) {
        Initialize(a);
    }

    public void Initialize(int a) {
        if (_init)
            throw new InvalidOperationException();
        _init = true;

        b = a;
    }

}
0
источник

Один из способов принудительного создания какого-либо конструктора - объявить только интерфейс Getters в интерфейсе, что может означать, что у класса-реализации должен быть метод, в идеале конструктор, для набора значений (private ly) для он.

0
источник

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

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

0
источник

Я снимаю этот поток из-за JavaScript, который передает его через прототипы.

Я также считаю, что конструктор не может преодолеть инициализацию своих полей. Особенно в С++, когда вы находитесь в конструкторе, вы не можете создать указатель "this". В С# это не так; Но это всего лишь недостаток принципов, и это преодолевает то, что происходит в конструкторе класса С++.

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

-1

Если я правильно понял OP, мы хотим обеспечить выполнение контракта, в котором GraphicsDeviceManager всегда инициализируется путем реализации классов. У меня была аналогичная проблема, и я искал лучшее решение, но это лучшее, что я могу придумать:

Добавьте в интерфейс SetGraphicsDeviceManager (GraphicsDeviceManager gdo), и таким образом классы реализации будут вынуждены писать логику, которая потребует вызова от конструктора.

-2
источник

Посмотрите другие вопросы по меткам или Задайте вопрос