首页 文章

定义构造函数签名的接口?

提问于
浏览
470

这是我第一次碰到这个问题,这很奇怪,但是:

如何在C#接口中定义构造函数?

Edit
有些人想要一个例子(它是一个游戏的's a free time project, so yes, it')

IDrawable
更新

为了能够更新(检查屏幕边缘等)并绘制自己,它将始终需要 GraphicsDeviceManager . 所以我想确保对象有引用它 . 这将属于构造函数 .

现在我写下来了,我认为我在这里实现的是 IObservableGraphicsDeviceManager 应该采用 IDrawable ......似乎要么我没有得到XNA框架,要么框架没有被很好地考虑 .

Edit
在接口的上下文中,我对构造函数的定义似乎有些混乱 . 实际上不能实例化接口,因此不需要构造函数 . 我想要定义的是构造函数的签名 . 正如接口可以定义某个方法的签名,接口可以定义构造函数的签名 .

17 回答

  • 5

    我发现解决这个问题的一种方法是将建筑分隔成一个单独的工厂 . 例如,我有一个名为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())
    

    希望有一天这可以帮助其他人,显然很多内部代码被删除,试图显示问题和解决方案

  • 5

    你没有 .

    构造函数是可以实现接口的类的一部分 . 接口只是类必须实现的方法的 Contract .

  • 298

    如果可以在接口中定义构造函数,那将非常有用 .

    鉴于接口是必须以指定方式使用的 Contract . 对于某些场景,以下方法可能是一种可行的替代方法:

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

    一个非常晚的贡献,展示了接口构造函数的另一个问题 . (我选择这个问题是因为它对问题有最明确的表述) . 假设我们可以:

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

    Would we say that the contract ICustomer is obeyed?

    那怎么样:

    interface ICustomer
    {
        ICustomer(string address);
    }
    
  • 131

    如前所述,您不能在接口上拥有构造函数 . 但是由于大约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构造函数,则可以创建一个类,该类定义您需要传入的所有字段的属性 .

  • 0

    接口的目的是强制执行某个对象签名 . 它应该明确地不关心对象如何在内部工作 . 因此,从概念的角度来看,接口中的构造函数并不真正有意义 .

    但有一些替代方案:

    • 创建一个充当最小默认实现的抽象类 . 该类应该具有您期望实现类的构造函数 .

    • 如果您不介意过度杀伤,请使用AbstractFactory模式并在工厂类接口中声明具有所需签名的方法 .

    • GraphicsDeviceManager 作为参数传递给 UpdateDraw 方法 .

    • 使用面向组件对象的编程框架来传递 GraphicsDeviceManager 进入需要它的对象的一部分 . 在我看来,这是一个非常实验性的解决方案 .

    您描述的情况一般不容易处理 . 类似的情况是业务应用程序中需要访问数据库的实体 .

  • -1

    你可以用泛型技巧来做到这一点,但它仍然容易受到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
    }
    
  • 87

    你不能 . 它偶尔会很痛苦,但无论如何你都无法使用普通技术来调用它 .

    在博客文章中,我建议static interfaces只能在泛型类型约束中使用 - 但可能非常方便,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)
        {
        }
    }
    
  • 19

    你不能 .

    接口定义其他对象实现的 Contract ,因此没有需要初始化的状态 .

    如果您有一些需要初始化的状态,则应考虑使用抽象基类 .

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

    不可能创建一个定义构造函数的接口,但它可以定义一个强制类型具有无参数构造函数的接口,尽管它是一个使用泛型的非常难看的语法...我实际上不太确定它确实是一个很好的编码模式 .

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

    希望这可以帮助 .

  • 0

    如果我正确理解OP,我们希望强制执行一个 Contract ,其中GraphicsDeviceManager总是通过实现类来初始化 . 我有一个类似的问题,我正在寻找一个更好的解决方案,但这是我能想到的最好的:

    将SetGraphicsDeviceManager(GraphicsDeviceManager gdo)添加到接口,这样实现类将被强制编写一个需要从构造函数调用的逻辑 .

  • 57

    解决此问题的一种方法是利用泛型和new()约束 .

    您可以将其表示为工厂类/接口,而不是将构造函数表示为方法/函数 . 如果在每个需要创建类对象的调用站点上指定new()泛型约束,则可以相应地传递构造函数参数 .

    对于您的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
    }
    

    另一种方法是使用lambda表达式作为初始化器 . 在调用层次结构的早期某个时刻,您将知道需要实例化哪些对象(即,在创建或获取对GraphicsDeviceManager对象的引用时) . 一旦你拥有它,通过lambda

    () => new Triangle(manager)
    

    后续方法,以便他们知道从那时起如何创建一个三角形 . 如果您无法确定所需的所有可能方法,则始终可以创建使用反射实现IDrawable的类型字典,并在字典中注册上面显示的lambda表达式,您可以将其存储在共享位置或传递给进一步的函数调用

  • 0

    Interfaces are Contract 接口不允许字段,即不需要初始化任何东西 . 接口只保持引用 . 接口必须要求派生类来保存内存 .

  • 0

    通用工厂方法似乎仍然是理想的 . 你会知道工厂需要一个参数,而这些参数会被传递给被实例化的对象的构造函数 .

    注意,这只是语法验证的伪代码,可能有一个运行时警告我在这里缺少:

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

    当然,您只需要通过工厂的创建实例来保证您始终拥有适当的初始化对象 . 也许使用像AutoFac这样的依赖注入框架会有意义; Update()可以"ask"用于新的GraphicsDeviceManager对象的IoC容器 .

  • 2

    强制某种构造函数的一种方法是在接口中声明只有 Getters ,这可能意味着实现类必须有一个方法,理想情况下是构造函数,才能为它设置值( private ly) .

  • 0

    虽然您无法在界面中定义构造函数签名,但我觉得值得一提的是,这可能是考虑抽象类的地方 . 抽象类可以定义未实现的(抽象)方法签名与接口相同,但也可以实现(具体)方法和构造函数 .

    缺点是,因为它是一种类,所以它不能用于接口可以的任何多继承类型场景 .

相关问题