当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《定义构造函数签名的接口?》经验,为你挑选了8个好方法。

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

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

编辑
有些人想要一个例子(这是一个空闲时间项目,所以是的,这是一个游戏)

IDrawable
+ Update
+ Draw

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

现在,我写下来我想我在这里实施的IObservableGraphicsDeviceManager应该采取的IDrawable......看来要么我不明白的XNA框架或框架不是想出来的非常好.

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



1> Jon Skeet..:

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

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


是的但是有什么问题,没有合适的Bar构造函数,因为它不能正确地满足接口.这就像说你不能在接口中定义方法,因为如果你不实现它,它将无法工作.
@Gravitas:有用与否,今天肯定无法使用.我怀疑如果这个功能出现,它需要比我们在评论中做的更精心的设计:)
@ user1721649:我有很多地方想要这个 - 几乎总是有通用约束.基本上,您希望能够在泛型方法中调用构造函数,以创建实现某个接口的泛型类型的实例.它确实*会很有用.
我确实可以看到问题,但您定义的所有其他方法也是如此.通常NotSupportedException是唯一的出路.
@boris:不同之处在于,在编译器保证的情况下,始终使用正常继承调用**.在这种情况下,有一些"应该"存在但不存在.
@jsimmons:不,你错过了我的观点.使用普通接口,从实现接口的类型派生的任何类型也会自动实现接口.那不是这种情况.
我明白了,但是那会是个问题吗?这只是意味着作为继承的一部分,您将需要实现某个构造函数。我的意思是,毕竟所有构建都是接口要定义的公共API的一部分。除非它暗示了编译器中更大的问题?
@jsimmons:嗯,这意味着类型在第一次发布后无法实现新接口,因为它可能会破坏现有的子类型.这些并非不可克服,但它们是需要考虑的额外事项 - 目前尚不存在的问题.

2> Gert Arnold..:

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

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


如果在ICustomer和IPerson中有相同签名和名称的方法,那么这个问题也存在.我不明白这有多大帮助.接口不是"仅我的定义".这是为了"不惜一切代价包括我"
我不知道这是一个什么问题,人们可以简单地制定一个规则,只允许链接具有相同构造函数签名的接口.这将是接口A实现"foo:int"而接口B实现"foo:string"的行为,它们只是不兼容.
@nawfal重点是接口永远不要求方法是_executed_,只是它应该存在.它永远不能保证国家.相反,"构造函数接口"确实要求在构造对象时完成某些事情(执行).当存在不同的接口时,永远不能保证这一点.
@GertArnold一个方法完成它的工作,而一个构造函数完成它的工作.我不明白这里有什么区别.接口签订了"我的实现应该在那里"的合同,而不是像"我的应该是唯一的实现".我会说为了一致性,这应该对构造函数,方法,属性有效
它让我想起没有虚拟继承的c ++多重继承问题
+1,虽然相当模糊,但这可能是最好的解释为什么接口,不仅在C#中,而且在许多语言(如Java)中,都不能有构造函数成员.

3> Dan..:

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


重要的是要强调,您不能创建"MustInitialize"类来涵盖每种情况,因为C#不允许多重继承.这意味着如果您的类继承了一个抽象类,那么它也不能继承另一个类.

4> Michael..:

你不能.

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

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


因为合同会约束你提供某种_behaviour_.如何使用接口意味着提取常见行为,而不依赖于状态(这将是一个实现细节).
为什么合同没有州?

5> Jeroen Landh..:

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

public interface IFoo where 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;
  }
}

希望这可以帮助.



6> Jeroen Landh..:

我回头看着这个问题,我想,也许我们正在以错误的方式解决这个问题.当涉及使用某些参数定义构造函数时,接口可能不是可行的方法......但是(抽象)基类是.

如果你在那里创建一个带有构造函数的基类,它接受你需要的参数,那么从中获取的每个类都需要提供它们.

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



7> JTtheGeek..:

我发现解决这个问题的一种方法是将建筑分隔成一个单独的工厂.例如,我有一个名为IQueueItem的抽象类,我需要一种方法将该对象转换为另一个对象(CloudQueueMessage).所以在IQueueItem界面上我有 -

public interface IQueueItem
{
    CloudQueueMessage ToMessage();
}

现在,我还需要一种方法让我的实际队列类将CloudQueueMessage转换回IQueueItem - 即需要静态构造,如IQueueItem objMessage = ItemType.FromMessage.相反,我定义了另一个接口IQueueFactory -

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

现在我终于可以在没有new()约束的情况下编写我的通用队列类,在我看来这是主要问题.

public class AzureQueue where 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;
    }
}

现在我可以创建一个满足我标准的实例

 AzureQueue objJobQueue = new JobQueue(new JobItemFactory())

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



8> 小智..:

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

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

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对象.

推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有