문제

그것은 이상한 것은 이번이 처음 나는 마음이 문제이지만,:

어떻게 정의 생성자에서는 C#하시겠습니까?

편집
몇 가지고 싶은 사람들이 예(무료 시간 프로젝트,그래서 그렇다,그것은 게임)

IDrawable
+업데이트
+립

업데이트할 수 있도록,(확인의 가장자리를 위한 스크린 등)및 그리기 자체는 그것은 항상 필요 GraphicsDeviceManager.그래서 내가 있는지 확인하려면 개체에 대한 참조가 있습니다.이 속에서 생성자입니다.

이제는 내가 쓴 이 아래로 나를 생각하는 내가 무엇을 구현하기 IObservableGraphicsDeviceManager 를 취해야한다 IDrawable...그것은 것 하나 하지 않을 얻을 XNA 프레임워크 또는 프레임워크가하지 않을 생각했습니다.

편집
있을 것으로 보인 혼란에 대한 내의 정의 생성자의 컨텍스트에서는 인터페이스입니다.인터페이스 할 수 있지 않은 실제로 인스턴스화하도록 필요하지 않습니다.내가 원하는 것을 정의하였 서명을 생성자입니다.정확히 같은 인터페이스를 정의할 수 있습니의 서명이 특정 메서드 인터페이스를 정의할 수 있습의 서명을 생성자입니다.

도움이 되었습니까?

해결책

이미 잘 알려진 바와 같이, 당신은 인터페이스에 생성자를 가질 수 없습니다. 그러나 이것은 약 7 년 후 Google에서 매우 순위가 높은 결과이기 때문에, 나는 여기에 칩을 낼 것이라고 생각했습니다. 특히 기존 인터페이스와 함께 추상적 인 기본 클래스를 사용할 수있는 방법을 보여주고 리팩토링의 양을 줄일 수 있습니다. 미래에 비슷한 상황에 필요합니다. 이 개념은 이미 일부 의견에서 힌트를 받았지만 실제로하는 방법을 보여줄 가치가 있다고 생각했습니다.

그래서 당신은 지금까지 이렇게 보이는 메인 인터페이스를 가지고 있습니다.

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

이제 시행하려는 생성자로 추상 클래스를 만듭니다. 실제로, 원래 질문을 작성한 이래로 이제 사용할 수 있으므로 여기에서 약간의 멋진 것을 얻고이 상황에서 제네릭을 사용하여 동일한 기능이 필요할 수 있지만 생성자 요구 사항이 다른 다른 인터페이스에 적응할 수 있습니다.

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

    }
}

이제 idrawable 인터페이스와 Mustinitialize Abstract 클래스에서 상속되는 새로운 클래스를 만들어야합니다.

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

여기서 멋진 점은 우리가 만든 새로운 드로우 가능한 클래스가 여전히 우리가 예상 할 수있는 것과 마찬가지로 행동한다는 것입니다.

Mustinitialize 생성자에게 둘 이상의 매개 변수를 전달 해야하는 경우 전달 해야하는 모든 필드의 속성을 정의하는 클래스를 만들 수 있습니다.

다른 팁

당신은 할 수 없습니다.그것은 때때로 고통이지만,당신이 할 수 없을 것이 전화를 사용하여 정상적인 기법을 어쨌든.

블로그 포스트에 나가 제안 정적 인터페이스 는 것에만 사용할 수 있에서 일반적인 입력 제약 조건-하지만 정말 편리하게,IMO.

중 하나는 지점에 대해한 경우 정의 생성자 인터페이스 내에 있는,당신은 문제가있을 파생하는 클래스:

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

인터페이스 생성자와의 또 다른 문제를 보여주는 매우 늦은 기여. (문제에 대한 가장 분명한 표현이 있기 때문에이 질문을 선택합니다). 우리가 가질 수 있다고 가정 해

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

당신은 할 수 없습니다.

인터페이스는 다른 객체가 구현 한 계약을 정의하므로 초기화해야 할 상태가 없습니다.

초기화 해야하는 상태가있는 경우 대신 추상 기본 클래스를 사용하는 것을 고려해야합니다.

생성자를 정의하는 인터페이스를 만들 수는 없지만 ~이다 유형이 매개 변수가없는 생성자를 갖도록 강요하는 인터페이스를 정의 할 수 있지만, 제네릭을 사용하는 매우 추악한 구문이라면 ... 실제로 좋은 코딩 패턴인지 확실하지 않습니다.

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

도움이 되었기를 바랍니다.

나는이 질문을 되돌아보고 있었고 나 자신에게 생각했다. 아마도 우리는이 문제를 잘못된 방식으로 무리에 끼치고있을 것이다. 인터페이스가 특정 매개 변수가있는 생성자를 정의하는 것과 관련이있을 때 인터페이스가 진행되는 방법이 아닐 수도 있지만 (추상) 기본 클래스는입니다.

필요한 매개 변수를 수용하는 생성자가있는 기본 클래스를 작성하는 경우, 그로부터 공급하는 데 필요한 모든 클래스.

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

내가 찾은이 문제를 해결하는 한 가지 방법은 건축을 별도의 공장으로 분리하는 것입니다. 예를 들어 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);
}

이제 마침내 새로운 () 제약 조건없이 제네릭 큐 클래스를 쓸 수 있습니다.

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

바라건대 이것은 언젠가 다른 사람의 다른 사람을 도울 수 있기를 바랍니다. 분명히 문제와 해결책을 보여주기 위해 많은 내부 코드가 제거되었습니다.

일반 공장에 접근 방식은 여전히 이상적인 것 같습니다 없습니다.당신이 알 수 있는 공장에 필요한 매개변수,그리고 그것은 그냥 그렇게 일어나는 이들 매개변수에 따라 전달된 생성되는 개체의 인스턴스화됩니다.

참고로,이 구문은 확인 의사 코드에있을 수있다,실시간주의가 나는 여기:

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

여만 원하는 만들기를 통해 인스턴스를 공장장 당신은 항상 적절하게 초기화 개체입니다.아마도를 사용하여 종속성을 주입 framework 아 AutoFac 감각을 만들 것;Update()수 있다"요청"IoC 컨테이너에 대한 새로운 GraphicsDeviceManager 개체입니다.

이 문제를 해결하는 한 가지 방법은 제네릭과 새로운 () 제약 조건을 활용하는 것입니다.

생성자를 메소드/기능으로 표현하는 대신 공장 클래스/인터페이스로 표현할 수 있습니다. 클래스의 객체를 생성 해야하는 모든 통화 사이트에 새로운 () 일반적인 제약 조건을 지정하면 그에 따라 생성자 인수를 전달할 수 있습니다.

당신의 edrawable 예 :

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
}

또 다른 방법은 Lambda 표현식을 초기화기로 사용하는 것입니다. Call 계층의 초기에 어느 시점에서, 당신은 어떤 개체를 인스턴스화 해야하는지 알게됩니다 (즉, GraphicsDeviceManager 개체에 대한 참조를 만들거나 참조 할 때). 당신이 그것을 갖는 즉시, 람다를 통과하십시오

() => new Triangle(manager) 

이후 방법으로는 그때부터 삼각형을 만드는 방법을 알게 될 것입니다. 필요한 모든 방법을 결정할 수없는 경우, 반사를 사용하여 Idrawable을 구현하는 유형의 사전을 만들고 공유 위치에 저장하거나 함께 전달할 수있는 사전에 위에 표시된 Lambda 표현식을 등록 할 수 있습니다. 추가 기능 호출.

제네릭 트릭 으로이 작업을 수행 할 수는 있지만 여전히 Jon Skeet이 쓴 것에 취약합니다.

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
}

인터페이스의 목적은 특정 객체 서명을 시행하는 것입니다. 객체가 내부적으로 어떻게 작동하는지 명시 적으로 염려하지 않아야합니다. 따라서 인터페이스의 생성자는 개념적 관점에서 실제로 의미가 없습니다.

그러나 몇 가지 대안이 있습니다.

  • 최소한의 기본 구현 역할을하는 추상 클래스를 만듭니다. 해당 클래스에는 클래스를 구현할 것으로 예상되는 생성자가 있어야합니다.

  • 오버 킬을 신경 쓰지 않으면 추상적 인 패턴을 사용하고 필수 서명이있는 공장 클래스 인터페이스의 메소드를 선언하십시오.

  • 통과하십시오 GraphicsDeviceManager 매개 변수로 Update 그리고 Draw 행동 양식.

  • 구성 객체 지향 프로그래밍 프레임 워크를 사용하여 GraphicsDeviceManager 그것을 요구하는 물체의 일부로. 이것은 제 생각에 꽤 실험적인 해결책입니다.

당신이 설명하는 상황은 일반적으로 다루기가 쉽지 않습니다. 비슷한 사례는 데이터베이스에 액세스 해야하는 비즈니스 애플리케이션의 엔티티입니다.

당신은 그렇지 않습니다.

생성자는 인터페이스를 구현할 수있는 클래스의 일부입니다. 인터페이스는 클래스가 구현 해야하는 방법의 계약 일뿐입니다.

인터페이스에서 생성자를 정의 할 수 있다면 매우 유용합니다.

인터페이스는 지정된 방식으로 사용해야하는 계약입니다. 다음 접근법은 일부 시나리오에 대한 실용적인 대안 일 수 있습니다.

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

}

어떤 종류의 생성자를 강요하는 한 가지 방법은 Getters 인터페이스에서 구현 클래스에 값 세트가 있어야하는 메소드, 이상적으로 생성자가 있어야 함을 의미 할 수 있습니다.privately).

인터페이스에서 생성자 서명을 정의 할 수는 없지만, 이것이 추상 클래스를 고려할 수있는 장소 일 수 있다고 언급 할 가치가 있다고 생각합니다. 초록 클래스는 인터페이스와 동일한 방식으로 구현되지 않은 (추상) 메소드 서명을 정의 할 수 있지만 (콘크리트) 방법 및 생성자도 구현할 수도 있습니다.

단점은 클래스 유형이기 때문에 인터페이스가 할 수있는 여러 상속 유형 시나리오에 사용할 수 없다는 것입니다.

나는 다음 패턴을 사용하여 방탄으로 만듭니다.

  • 베이스에서 자신의 클래스를 도출하는 개발자는 우연히 공개 액세스 가능한 생성자를 만들 수 없습니다.
  • 최종 클래스 개발자는 공통 생성 방법을 거쳐야합니다.
  • 모든 것이 유형 안전하고 주조가 필요하지 않습니다
  • 100% 유연하며 모든 곳에서 재사용 할 수 있으며, 여기서 자신의 기본 클래스를 정의 할 수 있습니다.
  • 시도해보십시오. 기본 클래스를 수정하지 않고는 끊을 수 없습니다 (오류 플래그가없는 쓸모없는 플래그를 정의하는 경우를 제외하고는 경고가 끝나게됩니다).

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

편집하다:다음은 인터페이스 추상화를 시행하는 도면 예제를 기반으로 한 예입니다.

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

OP를 올바르게 이해 한 경우 그래픽 드레비네이거너가 클래스를 구현하여 항상 초기화되는 계약을 시행하려고합니다. 나는 비슷한 문제가 있었고 더 나은 해결책을 찾고 있었지만 이것이 내가 생각할 수있는 최고입니다.

SetGraphicsDeviceManager (GraphicsDeviceManager GDO)를 인터페이스에 추가하면 구현 클래스가 생성자로부터 호출이 필요한 논리를 작성해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top