我可以禁止其他程序集继承自类吗?

时间:2013-07-02 20:40:45

标签: c#

我有这样的事情:

// This gets implemented by plugin authors to get callbacks about various things.
public interface ExternalPlugin
{
    // This gets called by the main application to tell the plugin some data
    // is available or similar.
    void DoStuff(SomeDataBlob blob);
}

// Data blob for v1 of API
public class SomeDataBlob
{
    internal SomeDataBlob(string prop) { Prop = prop; }
    // Some piece of data that V1 plugins need
    public string Prop { get; private set; }
}

// FUTURE!
// Data blob API v2 of API
public class SomeDataBlobV2 : SomeDataBlob
{
    // Can be passed to clients expecting SomeDataBlob no problem.
    internal SomeDataBlobV2(string prop, string prop2) :base(prop) { Prop2 = prop2; }
    // Some piece of data that V2 plugins need. V2 plugins can cast to this from
    // SomeDataBlob, but still can load successfully into older versions that support
    // only V1 of the API
    public string Prop2 { get; private set; }
}

我必须公开SomeDataBlob,以便它可以用作公共接口方法ExternalPlugin.DoStuff的成员。但是,我不希望允许客户端继承该类,因此容易受到脆弱的基类问题的影响。 (该类的所有衍生物应保存在同一组件中)

标记班级sealed太过分了,因为我认为删除sealed是一个重大的API变化;即使不是这样,一旦我发货SomeDataBlobV2客户仍然可以做错事并直接从SomeDataBlob继承。

有没有办法强制执行这种模式?

6 个答案:

答案 0 :(得分:9)

使类成为内部类,并显示接口。然后使用工厂模式创建正确的实现。

public interface ISomeDataBlob
{
}

internal class SomeDataBlob : ISomeDataBlob
{
}

public class BlobApiFactory
{
    ISomeDataBlob Create();
}

您隐藏了实现,但仍然允许用户访问所有内容。您甚至可以让用户更轻松地进行单元测试;)

编辑(回答OP的评论)

  

我真正想要的是采用一些参数的方法。我希望能够添加主应用程序可以在未来版本中提供的参数,如果API不破坏客户端。但我不希望客户端能够创建“参数”类/接口的实例,或者除了接收它的实例作为参数之外与其进行交互

您可以确保传递到库的所有对象都来自您的程序集,而不是隐藏API:

public class YourCoolService
{
    public void DoSomething(ISomeDataBlob blob)
    {
        if (blob.GetType().Assembly != Assembly.GetExecutingAssembly())
            throw new InvalidOperationException("We only support our own types");
    }
}

<强> EDIT2

注意到@RQDQ已经提供了解决方案(在回答你的评论时没有注意到)。如果这是您想要的解决方案,请接受他的回答。

答案 1 :(得分:3)

  /// <summary>
  /// This is a dummy constructor - it is just here to prevent classes in other assemblies
  /// from being derived from this class. 
  /// See http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2971840&SiteID=1
  /// </summary>
  internal MhlAdminLayer() { }

这个想法是让一个没有内部参数的构造函数。然后该类不能从中派生出来。

编辑:抱歉,评论中的链接不再有效。

编辑2: http://msdn.microsoft.com/en-us/library/vstudio/ms173115.aspx

“您可以通过将构造函数设为私有来阻止实例化类...”

答案 2 :(得分:2)

如果您不想使用密封的AND仍在使用类,那么您可以在运行时强制执行此操作。换句话说,在您的API边界,检查所涉及的类并确保它们来自您的程序集。

一个简单的例子:

public void Bar(Foo foo)
{
    if (foo.GetType().Assembly != this.GetType().Assembly)
        throw new InvalidOperationException("It's not one of mine!");
}

public class Foo
{
}

答案 3 :(得分:1)

据我所知,接口是实现此目的的方法。这将是一个API破坏性的变化,但这意味着你可以做你想做的事。

public interface ExternalPlugin
{
    void DoStuff(ISomeDataBlob blob);
}
// Interfaces:
public interface IDataBlob
{
    string Prop { get; }
}
public interface IDataBlobV2 : IDataBlob
{
    string Prop2 { get; }
}
// Data blob for v1 of API
internal class SomeDataBlob : IDataBlob
{
    internal SomeDataBlob(string prop) { Prop = prop; }
    public string Prop { get; private set; }
}
// FUTURE!
// Data blob API v2 of API
public class SomeDataBlobV2 : SomeDataBlob, IDataBlobV2
{
    // Can be passed to clients expecting SomeDataBlob no problem.
    internal SomeDataBlobV2(string prop, string prop2) : base(prop) { Prop2 = prop2; }
    public string Prop2 { get; private set; }
}

然后使对象使用工厂模式,例如

public static class DataBlobFactory
{
    public IDataBlob GetDataBlob(string prop)
    {
        return new SomeDataBlob(prop);
    }
    // so on.
}

答案 4 :(得分:0)

我要做的是制作某种工厂类,该工厂类公开一个接口,该接口将传递客户端使用的特定API的任何版本的实例,并使用内部类隐藏实现

你也可以使用约束来使它更容易使用,然后客户端可以只放置他们正在寻找的对象类型

public interface IBlobV1 { /*public stuff for V1 here*/ } 
internal class BlobV1: IBlobV1 {/*v1 implementation*/ }

public interface IBlobV2 : IBlobV1 {/*public interface for V2 here*/ }
internal class BlobV2:BlobV1,IBlobV2 {/*v2 implementation*/} 



public sealed BlobFactory 
{
    public IBlobV1 CreateVersion1Blob(){/* implementation */} 
    public IBlobV2 CreateVersion2Blob(){/* implementation */}
    public T CreateBlob<T>() 
           where T: IBlobV1
    { /* implementation */}
}

答案 5 :(得分:0)

SomeDataBlob无法继承,因为它的唯一构造函数是内部。如果您尝试在客户端应用程序中实现派生类:

class SomeDataBlobClient : SomeDataBlob
{
    SomeDataBlobClient():base("TEST")
    {

    }
}

您将收到以下错误:

  

类型'ClassLibrary1.SomeDataBlob'没有定义构造函数

看来你解决了自己的问题。