Bor*_*ens 526 c# constructor interface
这是我第一次碰到这个问题,这很奇怪,但是:
如何在C#接口中定义构造函数?
编辑
有些人想要一个例子(这是一个空闲时间项目,所以是的,这是一个游戏)
IDrawable
+ Update
+ Draw
为了能够更新(检查屏幕边缘等)并绘制自己,它总是需要一个GraphicsDeviceManager.所以我想确保对象有引用它.这将属于构造函数.
现在,我写下来我想我在这里实施的IObservable和GraphicsDeviceManager应该采取的IDrawable......看来要么我不明白的XNA框架或框架不是想出来的非常好.
编辑
在接口的上下文中,我对构造函数的定义似乎有些混乱.实际上不能实例化接口,因此不需要构造函数.我想要定义的是构造函数的签名.正如接口可以定义某个方法的签名,接口可以定义构造函数的签名.
Jon*_*eet 313
你不能.它偶尔会很痛苦,但无论如何你都无法使用普通技术来调用它.
在博客文章中,我建议静态接口只能在泛型类型约束中使用 - 但可能非常方便,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)
{
}
}
Run Code Online (Sandbox Code Playgroud)
Ger*_*old 135
一个非常晚的贡献,展示了接口构造函数的另一个问题.(我选择这个问题是因为它对问题有最明确的阐述).假设我们可以:
interface IPerson
{
IPerson(string name);
}
interface ICustomer
{
ICustomer(DateTime registrationDate);
}
class Person : IPerson, ICustomer
{
Person(string name) { }
Person(DateTime registrationDate) { }
}
Run Code Online (Sandbox Code Playgroud)
按惯例,"接口构造函数"的实现由类型名称替换.
现在创建一个实例:
ICustomer a = new Person("Ernie");
Run Code Online (Sandbox Code Playgroud)
我们会说合同ICustomer是否得到遵守?
那怎么样:
interface ICustomer
{
ICustomer(string address);
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*Dan 131
如前所述,您不能在接口上拥有构造函数.但是由于大约7年后谷歌的排名很高,我认为我会在这里筹码 - 特别是为了展示你如何使用抽象基类与你现有的界面相结合,并可能减少重构的数量未来需要类似的情况.这个概念已经在一些评论中被暗示过,但我认为值得展示如何实际做到这一点.
所以到目前为止你的主界面看起来像这样:
public interface IDrawable
{
void Update();
void Draw();
}
Run Code Online (Sandbox Code Playgroud)
现在使用要强制执行的构造函数创建一个抽象类.实际上,既然它从你编写原始问题开始就可以使用了,我们可以在这里稍微看一下并在这种情况下使用泛型,这样我们就可以将其调整到可能需要相同功能但具有不同构造函数要求的其他接口:
public abstract class MustInitialize<T>
{
public MustInitialize(T parameters)
{
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您需要创建一个继承自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
}
}
Run Code Online (Sandbox Code Playgroud)
然后只需创建一个Drawable实例,你就可以了:
IDrawable drawableService = new Drawable(myGraphicsDeviceManager);
Run Code Online (Sandbox Code Playgroud)
这里很酷的是我们创建的新Drawable类的行为与我们对IDrawable的期望一样.
如果需要将多个参数传递给MustInitialize构造函数,则可以创建一个类,该类定义您需要传入的所有字段的属性.
Mic*_*ael 62
你不能.
接口定义其他对象实现的合同,因此没有需要初始化的状态.
如果您有一些需要初始化的状态,则应考虑使用抽象基类.
Jer*_*eer 20
创建一个定义构造函数的接口是不可能的,但是可以定义一个强制类型具有无参数构造函数的接口,尽管它是一个使用泛型的非常难看的语法...我实际上不太确定它确实是一个很好的编码模式.
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
}
Run Code Online (Sandbox Code Playgroud)
另一方面,如果要测试类型是否具有无参数构造函数,则可以使用反射来执行此操作:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助.
Jer*_*eer 20
我回头看着这个问题,我想,也许我们正在以错误的方式解决这个问题.当涉及使用某些参数定义构造函数时,接口可能不是可行的方法......但是(抽象)基类是.
如果你在那里创建一个带有构造函数的基类,它接受你需要的参数,那么从中获取的每个类都需要提供它们.
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
{
}
}
Run Code Online (Sandbox Code Playgroud)
我发现解决这个问题的一种方法是将建筑分隔成一个单独的工厂.例如,我有一个名为IQueueItem的抽象类,我需要一种方法将该对象转换为另一个对象(CloudQueueMessage).所以在IQueueItem界面上我有 -
public interface IQueueItem
{
CloudQueueMessage ToMessage();
}
Run Code Online (Sandbox Code Playgroud)
现在,我还需要一种方法让我的实际队列类将CloudQueueMessage转换回IQueueItem - 即需要静态构造,如IQueueItem objMessage = ItemType.FromMessage.相反,我定义了另一个接口IQueueFactory -
public interface IQueueItemFactory<T> where T : IQueueItem
{
T FromMessage(CloudQueueMessage objMessage);
}
Run Code Online (Sandbox Code Playgroud)
现在我终于可以在没有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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我可以创建一个满足我标准的实例
AzureQueue<Job> objJobQueue = new JobQueue(new JobItemFactory())
Run Code Online (Sandbox Code Playgroud)
希望有一天这可以帮助其他人,显然很多内部代码被删除,试图显示问题和解决方案
解决此问题的一种方法是利用泛型和 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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在当你使用它时:
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
}
Run Code Online (Sandbox Code Playgroud)
您甚至可以使用显式接口实现将所有创建方法集中在一个类中:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
要使用它:
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
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用 lambda 表达式作为初始值设定项。在调用层次结构早期的某个时刻,您将知道需要实例化哪些对象(即,当您创建或获取对 GraphicsDeviceManager 对象的引用时)。一旦你拥有它,就传递 lambda
() => new Triangle(manager)
Run Code Online (Sandbox Code Playgroud)
到后续方法,以便他们从那时起知道如何创建三角形。如果您无法确定您需要的所有可能方法,您始终可以创建一个使用反射实现 IDrawable 的类型的字典,并在字典中注册上面显示的 lambda 表达式,您可以将其存储在共享位置或传递给进一步的函数调用。
通用工厂方法似乎仍然是理想的.您会知道工厂需要一个参数,而这些参数会被传递给正在实例化的对象的构造函数.
注意,这只是语法验证的伪代码,可能有一个运行时警告我在这里缺少:
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
}
Run Code Online (Sandbox Code Playgroud)
可能的用法示例:
public void DoSomething()
{
var myUpdateObject = GetDrawingObject<Update>(new GraphicsDeviceManager());
var myDrawObject = GetDrawingObject<Draw>(null);
}
Run Code Online (Sandbox Code Playgroud)
当然,您只需要通过工厂的创建实例来保证您始终拥有适当的初始化对象.也许使用像AutoFac这样的依赖注入框架是有意义的; Update()可以"询问"IoC容器中的新GraphicsDeviceManager对象.
| 归档时间: |
|
| 查看次数: |
287915 次 |
| 最近记录: |