Pergunta

É estranho que esta é a primeira vez que eu batido para este problema, mas:

Como você define um construtor em uma interface C #?

Editar
Algumas pessoas queriam um exemplo (que é um projeto de tempo livre, por isso, sim, é um jogo)

IDrawable
+ Atualização
+ Desenhar

Para poder Update (cheque de borda da tela etc) e desenhar a si mesmo que vai sempre precisar de uma GraphicsDeviceManager. Então, eu quero ter certeza que o objeto tem uma referência a ele. Este pertenceria no construtor.

Agora que eu escrevi isso para baixo Eu acho que o que eu estou implementando aqui é IObservable eo GraphicsDeviceManager deve tomar a IDrawable ... Parece quer Eu não entendo o XNA Framework, ou o quadro não é pensado muito bem.

Editar
Parece haver alguma confusão sobre a minha definição de construtor no contexto de uma interface. Uma interface pode realmente não ser instanciado por isso não precisa de um construtor. O que eu queria para definir se uma assinatura para um construtor. Exatamente como uma interface pode definir uma assinatura de um determinado método, a interface poderia definir a assinatura de um construtor.

Foi útil?

Solução

Como já foi bem observado, você pode não ter construtores em uma interface. Mas uma vez que este é um tal resultado altamente classificado no Google cerca de 7 anos depois, eu pensei que eu iria chip aqui - especificamente para mostrar como você pode usar uma classe base abstrata em conjunto com a sua interface existente e talvez diminuir a quantidade de refatoração necessário no futuro para situações semelhantes. Este conceito já foi sugerida em alguns dos comentários, mas eu pensei que seria vale a pena mostrar como realmente fazê-lo.

Então, você tem a sua interface principal que se parece com isso até agora:

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

Agora crie uma classe abstrata com o construtor você deseja impor. Na verdade, uma vez que é agora disponível desde o tempo que você escreveu sua pergunta original, podemos ficar um pouco de fantasia aqui e usar os genéricos nesta situação para que possamos adaptar isso para outras interfaces que podem precisar a mesma funcionalidade, mas têm diferentes requisitos do construtor:

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

    }
}

Agora você vai precisar para criar uma nova classe que herda a interface IDrawable ea classe abstrata 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
    }
}

Em seguida, basta criar uma instância de Drawable e você está pronto para ir:

IDrawable drawableService = new Drawable(myGraphicsDeviceManager);

A coisa legal aqui é que a nova classe Drawable ainda criado se comporta exatamente como o que seria de esperar de um IDrawable.

Se você precisa passar mais de um parâmetro para o construtor MustInitialize, você pode criar uma classe que define as propriedades para todos os campos que você precisa para passar.

Outras dicas

Você não pode. É ocasionalmente, uma dor, mas você não seria capaz de chamá-lo usando técnicas normais de qualquer maneira.

Em um post no blog eu sugeri estática interfaces de que só seria utilizável em restrições de tipo genérico -., mas poderia ser realmente útil, IMO

Um ponto sobre se você pode definir um construtor dentro de uma interface, você teria aulas decorrentes de problemas:

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

Uma contribuição muito tarde demonstrando um outro problema com construtores interligados. (I escolher esta pergunta, porque ele tem a articulação mais clara do problema). Suponha que poderia ter:

interface IPerson
{
    IPerson(string name);
}

interface ICustomer
{
    ICustomer(DateTime registrationDate);
}

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

Quando, por convenção a implementação do "construtor de interface" é substituído pelo nome do tipo.

Agora faça uma instância:

ICustomer a = new Person("Ernie");

diríamos que o ICustomer contrato é obedecida?

E o que dizer isto:

interface ICustomer
{
    ICustomer(string address);
}

Você não pode.

Interfaces definir contratos que outros objetos implementar e, portanto, não têm estado que precisa ser inicializado.

Se você tiver algum estado que precisa ser inicializado, você deve considerar o uso de uma classe base abstrata vez.

Não é possível criar uma interface que define os construtores, mas é possível definir uma interface que as forças de um tipo de ter um construtor paramerterless, embora seja uma sintaxe muito feio que usa genéricos ... Eu não sou realmente tão certo de que ele é realmente um padrão de codificação bom.

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
}

Por outro lado, se você quiser testar se um tipo tem um construtor paramerterless, você pode fazer isso usando a reflexão:

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

Espero que isso ajude.

Eu estava olhando para essa questão e eu pensei para mim mesmo, talvez estamos aproaching este problema de forma errada. Interfaces pode não ser o caminho a percorrer quando se trata de definir um construtor com determinados parâmetros ... mas uma classe (abstrato) base é.

Se você criar uma classe base com um construtor lá que aceita os parâmetros que você precisa, cada classe que derrives de que necessita para fornecê-los.

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

Uma maneira de resolver este problema que eu encontrei é para separar a construção em uma fábrica separada. Por exemplo, eu tenho uma classe abstrata chamada IQueueItem, e eu preciso de uma maneira de traduzir esse objeto de e para outro objeto (CloudQueueMessage). Então, na interface IQueueItem eu tenho -

public interface IQueueItem
{
    CloudQueueMessage ToMessage();
}

Agora, eu também precisa de um caminho para a minha classe fila real para traduzir uma volta CloudQueueMessage a um IQueueItem - ou seja, a necessidade de uma construção estática como IQueueItem objMessage = ItemType.FromMessage. Em vez disso eu definida outra IQueueFactory Interface -

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

Agora posso finalmente escrever a minha classe de fila genérico sem a nova restrição (), que no meu caso foi a questão principal.

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

Agora posso criar uma instância que satisfaça os critérios de me

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

espero que isso ajude alguém algum dia, obviamente, um monte de código interno removido para tentar mostrar o problema ea solução

A abordagem fábrica genérico ainda parece ideal. Você saberia que a fábrica requer um parâmetro, e que seria apenas acontecer que esses parâmetros são passados ??para o construtor do objeto a ser instanciado.

Note, este é apenas sintaxe verificada pseudo código, pode haver uma ressalva de tempo de execução que estou perdendo aqui:

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
}

Um exemplo de possível uso:

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

Com certeza, você só quer o criar instâncias através da fábrica para garantir que você sempre tem um objeto adequadamente inicializado. Talvez usando um quadro de injeção de dependência como Autofac faria sentido; Update () poderia "pedir" o contêiner IoC para um novo objeto GraphicsDeviceManager.

Uma maneira de resolver este problema é genéricos de alavancagem e a nova restrição ().

Em vez de expressar seu construtor como um método / função, você pode expressá-la como uma classe de fábrica / interface. Se você especificar o novo () restrição genérica em cada site de chamada que as necessidades para criar um objeto de sua classe, você será capaz de passar argumentos do construtor em conformidade.

Para o seu exemplo 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);
    } 
}

Agora, quando você usá-lo:

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
}

Você pode até concentrar todos os métodos de criação em uma única classe usando a implementação de interface explícita:

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

Para usá-lo:

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
}

Outra maneira é usando expressões lambda como initializers. Em algum momento no início da hierarquia de chamadas, você vai saber quais objetos você precisará instanciar (ou seja, quando você está criando ou obter uma referência ao seu objeto GraphicsDeviceManager). Assim que você tem isso, passar o lambda

() => new Triangle(manager) 

para métodos subseqüentes para que eles saibam como criar um triângulo a partir de então. Se você não pode determinar todos os métodos possíveis que você vai precisar, você sempre pode criar um dicionário de tipos que implementam IDrawable usando a reflexão e registrar a expressão lambda mostrado acima em um dicionário que você pode armazenar em um local compartilhado ou passar para chamadas outra função.

Você pode fazer isso com os genéricos truque, mas ainda é vulnerável ao que Jon Skeet escreveu:

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

classe que implementa essa interface deve ter construtor sem parâmetros:

public class A : IHasDefaultConstructor<A> //Notice A as generic parameter
{
    public A(int a) { } //compile time error
}

O propósito de uma interface é para impor uma determinada assinatura objeto. Deve explicitamente não estar preocupado com a forma como um objeto funciona internamente. Portanto, um construtor em uma interface realmente não faz sentido do ponto de vista conceitual.

Existem algumas alternativas que:

  • Criar uma classe abstrata que atua como uma implementação padrão mínimo. Essa classe deve ter os construtores que você espera classes de implementação de ter.

  • Se você não se importa o exagero, use o padrão AbstractFactory e declarar um método na interface de classe de fábrica que tem o necessário assinaturas.

  • Passe o GraphicsDeviceManager como parâmetro para os métodos Update e Draw.

  • Use um objeto composicional Oriented Programming quadro para passar o GraphicsDeviceManager para a parte do objeto que exige. Esta é uma solução bastante experimental na minha opinião.

A situação que você descreve não é fácil de manusear em geral. Um caso semelhante seria entidades em um aplicativo de negócios que requerem acesso ao banco de dados.

você não.

o construtor faz parte da classe que pode implementar uma interface. A interface é apenas um contrato de métodos da classe deve implementar.

Seria muito útil se fosse possível definir construtores em interfaces.

Uma vez que uma interface é um contrato que deve ser utilizado na forma especificada. A seguinte abordagem pode ser uma alternativa viável para alguns cenários:

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

}

Uma forma de forçar algum tipo de construtor é a declarar apenas Getters na interface, que poderia, então, significa que a classe de implementação deve ter um método, de preferência um construtor, para ter o valor definido (privately) para ele.

Enquanto você não pode definir uma assinatura de construtor em uma interface, eu sinto que vale a pena mencionar que este pode ser um ponto a considerar uma classe abstrata. As classes abstratas podem definir por implementar assinaturas (abstrato) Método da mesma forma como uma interface, mas também pode ter implementado métodos e construtores (concreto).

A desvantagem é que, porque é um tipo de classe, ele não pode ser usado para qualquer um dos vários cenários tipo de herança que uma lata de interface.

Eu uso o seguinte padrão para torná-lo à prova de balas.

  • Um desenvolvedor que deriva sua classe a partir da base não pode acidentalmente criar um construtor público acessível
  • O desenvolvedor classe final são forçados a passar pelo comum criar método
  • Tudo é tipo seguro, há castings são obrigados
  • É 100% flexível e pode ser reutilizado em todos os lugares, onde você pode definir a sua própria base classe.
  • Experimente você não pode quebrá-lo sem fazer modificações para as classes base (excepto se você definir uma bandeira obsoleto sem conjunto sinalizador de erro de verdade, mas mesmo assim você acaba com um aviso)

        public abstract class Base<TSelf, TParameter>
        where TSelf : Base<TSelf, TParameter>, new()
    {
        protected const string FactoryMessage = "Use YourClass.Create(...) instead";
        public static TSelf Create(TParameter parameter)
        {
            var me = new TSelf();
            me.Initialize(parameter);
    
            return me;
        }
    
        [Obsolete(FactoryMessage, true)]
        protected Base()
        {
        }
    
    
    
        protected virtual void Initialize(TParameter parameter)
        {
    
        }
    }
    
    public abstract class BaseWithConfig<TSelf, TConfig>: Base<TSelf, TConfig>
        where TSelf : BaseWithConfig<TSelf, TConfig>, new()
    {
        public TConfig Config { get; private set; }
    
        [Obsolete(FactoryMessage, true)]
        protected BaseWithConfig()
        {
        }
        protected override void Initialize(TConfig parameter)
        {
            this.Config = parameter;
        }
    }
    
    public class MyService : BaseWithConfig<MyService, (string UserName, string Password)>
    {
        [Obsolete(FactoryMessage, true)]
        public MyService()
        {
        }
    }
    
    public class Person : Base<Person, (string FirstName, string LastName)>
    {
        [Obsolete(FactoryMessage,true)]
        public Person()
        {
        }
    
        protected override void Initialize((string FirstName, string LastName) parameter)
        {
            this.FirstName = parameter.FirstName;
            this.LastName = parameter.LastName;
        }
    
        public string LastName { get; private set; }
    
        public string FirstName { get; private set; }
    }
    
    
    
    [Test]
    public void FactoryTest()
    {
        var notInitilaizedPerson = new Person(); // doesn't compile because of the obsolete attribute.
        Person max = Person.Create(("Max", "Mustermann"));
        Assert.AreEqual("Max",max.FirstName);
    
        var service = MyService.Create(("MyUser", "MyPassword"));
        Assert.AreEqual("MyUser", service.Config.UserName);
    }
    

EDIT: E aqui está um exemplo com base no seu exemplo de desenho que mesmo Enforces interface de abstração

        public abstract class BaseWithAbstraction<TSelf, TInterface, TParameter>
        where TSelf : BaseWithAbstraction<TSelf, TInterface, TParameter>, TInterface, new()
    {
        [Obsolete(FactoryMessage, true)]
        protected BaseWithAbstraction()
        {
        }

        protected const string FactoryMessage = "Use YourClass.Create(...) instead";
        public static TInterface Create(TParameter parameter)
        {
            var me = new TSelf();
            me.Initialize(parameter);

            return me;
        }

        protected virtual void Initialize(TParameter parameter)
        {

        }
    }



    public abstract class BaseWithParameter<TSelf, TInterface, TParameter> : BaseWithAbstraction<TSelf, TInterface, TParameter>
        where TSelf : BaseWithParameter<TSelf, TInterface, TParameter>, TInterface, new()
    {
        protected TParameter Parameter { get; private set; }

        [Obsolete(FactoryMessage, true)]
        protected BaseWithParameter()
        {
        }
        protected sealed override void Initialize(TParameter parameter)
        {
            this.Parameter = parameter;
            this.OnAfterInitialize(parameter);
        }

        protected virtual void OnAfterInitialize(TParameter parameter)
        {
        }
    }


    public class GraphicsDeviceManager
    {

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

    internal abstract class Drawable<TSelf> : BaseWithParameter<TSelf, IDrawable, GraphicsDeviceManager>, IDrawable 
        where TSelf : Drawable<TSelf>, IDrawable, new()
    {
        [Obsolete(FactoryMessage, true)]
        protected Drawable()
        {
        }

        public abstract void Update();
        public abstract void Draw();
    }

    internal class Rectangle : Drawable<Rectangle>
    {
        [Obsolete(FactoryMessage, true)]
        public Rectangle()
        {
        }

        public override void Update()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }

        public override void Draw()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }
    }
    internal class Circle : Drawable<Circle>
    {
        [Obsolete(FactoryMessage, true)]
        public Circle()
        {
        }

        public override void Update()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }

        public override void Draw()
        {
            GraphicsDeviceManager manager = this.Parameter;
            // TODo  manager
        }
    }


    [Test]
    public void FactoryTest()
    {
        // doesn't compile because interface abstraction is enforced.
        Rectangle rectangle = Rectangle.Create(new GraphicsDeviceManager());

        // you get only the IDrawable returned.
        IDrawable service = Circle.Create(new GraphicsDeviceManager());
    }

Se eu entendi corretamente OP, queremos fazer cumprir um contrato onde GraphicsDeviceManager é sempre inicializado através da implementação de classes. Eu tive um problema semelhante e eu estava procurando uma solução melhor, mas isso é o melhor que eu posso pensar de:

Adicionar um SetGraphicsDeviceManager (GraphicsDeviceManager GDO) para a interface, e de que maneira as classes de implementação será forçado a escrever uma lógica que será exigem uma chamada do construtor.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top