这是我第一次碰到这个问题,这很奇怪,但是:
如何在C#接口中定义构造函数?
编辑
有些人想要一个例子(这是一个空闲时间项目,所以是的,这是一个游戏)
IDrawable
+ Update
+ Draw
为了能够更新(检查屏幕边缘等)并绘制自己,它总是需要一个GraphicsDeviceManager
.所以我想确保对象有引用它.这将属于构造函数.
现在,我写下来我想我在这里实施的IObservable
和GraphicsDeviceManager
应该采取的IDrawable
......看来要么我不明白的XNA框架或框架不是想出来的非常好.
编辑
在接口的上下文中,我对构造函数的定义似乎有些混乱.实际上不能实例化接口,因此不需要构造函数.我想要定义的是构造函数的签名.正如接口可以定义某个方法的签名,接口可以定义构造函数的签名.
你不能.它偶尔会很痛苦,但无论如何你都无法使用普通技术来调用它.
在博客文章中,我建议静态接口只能在泛型类型约束中使用 - 但可能非常方便,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); }
如前所述,您不能在接口上拥有构造函数.但是由于大约7年后谷歌的排名很高,我认为我会在这里筹码 - 特别是为了展示你如何使用抽象基类与你现有的界面相结合,并可能减少重构的数量未来需要类似的情况.这个概念已经在一些评论中被暗示过,但我认为值得展示如何实际做到这一点.
所以到目前为止你的主界面看起来像这样:
public interface IDrawable { void Update(); void Draw(); }
现在使用要强制执行的构造函数创建一个抽象类.实际上,既然它从你编写原始问题开始就可以使用了,我们可以在这里稍微看一下并在这种情况下使用泛型,这样我们就可以将其调整到可能需要相同功能但具有不同构造函数要求的其他接口:
public abstract class MustInitialize{ public MustInitialize(T parameters) { } }
现在,您需要创建一个继承自IDrawable接口和MustInitialize抽象类的新类:
public class Drawable : MustInitialize, 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构造函数,则可以创建一个类,该类定义您需要传入的所有字段的属性.
你不能.
接口定义其他对象实现的合同,因此没有需要初始化的状态.
如果您有一些需要初始化的状态,则应考虑使用抽象基类.
创建一个定义构造函数的接口是不可能的,但是可以定义一个强制类型具有无参数构造函数的接口,尽管它是一个使用泛型的非常难看的语法...我实际上不太确定它确实是一个很好的编码模式.
public interface IFoowhere T : new() { void SomeMethod(); } public class Foo : IFoo { // This will not compile public Foo(int x) { } #region ITest 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 IQueueItemFactorywhere T : IQueueItem { T FromMessage(CloudQueueMessage objMessage); }
现在我终于可以在没有new()约束的情况下编写我的通用队列类,在我看来这是主要问题.
public class AzureQueuewhere T : IQueueItem { private IQueueItemFactory _objFactory; public AzureQueue(IQueueItemFactory objItemFactory) { _objFactory = objItemFactory; } public T GetNextItem(TimeSpan tsLease) { CloudQueueMessage objQueueMessage = _objQueue.GetMessage(tsLease); T objItem = _objFactory.FromMessage(objQueueMessage); return objItem; } }
现在我可以创建一个满足我标准的实例
AzureQueueobjJobQueue = new JobQueue(new JobItemFactory())
希望有一天这可以帮助其他人,显然很多内部代码被删除,试图显示问题和解决方案
通用工厂方法似乎仍然是理想的.您会知道工厂需要一个参数,而这些参数会被传递给正在实例化的对象的构造函数.
注意,这只是语法验证的伪代码,可能有一个运行时警告我在这里缺少:
public interface IDrawableFactory { TDrawable GetDrawingObject(GraphicsDeviceManager graphicsDeviceManager) where TDrawable: class, IDrawable, new(); } public class DrawableFactory : IDrawableFactory { public TDrawable GetDrawingObject (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(new GraphicsDeviceManager()); var myDrawObject = GetDrawingObject (null); }
当然,您只需要通过工厂的创建实例来保证您始终拥有适当的初始化对象.也许使用像AutoFac这样的依赖注入框架是有意义的; Update()可以"询问"IoC容器中的新GraphicsDeviceManager对象.