寻找隐藏类基类的方法

时间:2010-09-21 23:00:17

标签: c# oop class class-design

我正在建立一个库,我有一个类似于以下的模式:

public class Foo : Foo_Impl
{
}

public class Foo_Impl
{
}

我不希望其他开发人员意外使用Foo_Impl类。有什么选择可以隐藏这个?我也想将它隐藏在它所定义的同一个程序集中的其他类中。理想情况下我喜欢这样做:

public class Foo : Foo_Impl
{
  private class Foo_Impl
  {
  }
}

但是由于各种原因,这不起作用。

7 个答案:

答案 0 :(得分:9)

我还没有看到这个建议,所以我想我会添加我的$ .02。

如何使用合成而不是继承? Foo_Impl的一个实例可以由Foo维护,并且永远不会对外界可见(因为Foo_impl对于程序集是私有的)。可以根据需要通过接口将调用传递给Foo_Impl函数。您可以获得相同的功能,而不会遇到任何设计难题。

private class Foo_Impl
{
    public void DoSomething() { }
}

public class Foo
{
    private Foo_Impl _implementation;

    public Foo() { _implementation = new Foo_Impl(); }

    public void DoSomething() { _implementation.DoSomething(); }
}

就“将其隐藏在程序集中的其他类中”而言,如果你真的认为合适,可以将它作为嵌套类。

答案 1 :(得分:5)

使Foo_Impl成为一个抽象类。这不会阻止其他开发人员从中派生,但它将无法直接创建Foo_Impl的实例 - 它需要通过创建派生对象(例如Foo)来实例化。

public abstract class Foo_Impl
{
    public Foo_Impl()
    {
    }
}

public class Foo : Foo_Impl
{
    public Foo() // implicitly calls the base class's constructor
    {
    }
}

-

var works = new Foo(); // Works
var error = new Foo_Impl(); // Compiler error

根据Thomas Levesque的建议,您还可以选择将抽象构造函数设为内部:

public abstract class Foo_Impl
{
    internal Foo_Impl()
    {
    }
}

public class Foo : Foo_Impl
{
    public Foo() // implicitly calls the base class's constructor
    {
    }
}

这将阻止开发人员从程序集外部继承Foo_Impl

答案 2 :(得分:4)

没有办法完全隐藏它。如果Foo将公开,其基类也必须公开。但是,您可以通过使构造函数受到保护来阻止其他开发人员创建Foo_Impl的实例。这样构造函数只能从派生类中使用。

public class Foo_Impl
{
    protected Foo_Impl()
    {
    }
}

public class Foo : Foo_Impl
{
    public Foo() // implicitly calls the base class's protected constructor
    {
    }
}

如果您不希望人们能够创建自己的Foo_Impl派生类,请将构造函数设为内部

答案 3 :(得分:4)

您拥有的选项是:

  • 使Foo_Impl成为抽象。开发人员将无法直接实例化Foo_Impl;他们必须宣布一个Foo。但是,Foo_Impl仍然可以用作方法参数或泛型类型参数,开发人员可以从Foo_Impl派生自己的类。

  • 将Foo_Impl设为内部,并将它和Foo放在要隐藏Foo_Impl的任何其他类的单独库中。在该程序集之外工作的开发人员将无法看到Foo_Impl。但是,开发人员可能能够反映类型并在运行时具有足够的CAS权限时实例化一个类型,并且任何有权访问程序集源代码的开发人员都可以在该程序集中新建一个Foo_Impl。

  • 使Foo_Impl的所有构造函数都受到保护或内部构造。受保护的构造函数只能由派生类访问,而内部构造函数只能在同一个程序集中访问。无论哪种方式,Foo_Impl都无法由局外人实例化。但是,开发人员仍然可以将该类用作参数或泛型类型。

  • 使用[Obsolete]属性将Mark Foo_Impl的构造函数或整个类作为Obsolete,指定使用此类应该触发错误。这是一种破解,但你可以阻止编译“错误”代码,并使用属性的Message参数来指定应该使用什么。但是,要注意你装饰的东西;如果你装饰整个类,我认为编译器不会让你对类做任何事情,包括继承它。装饰施工人员应该阻止大多数程序员。

答案 4 :(得分:3)

这可能听起来很愚蠢,但有一个非常简单的解决方案:记录正确的用法。如果不应该实例化Foo_Impl,那么向类添加注释,这样做。将它包含在关于该类的任何其他文档中。

即使您在此处实施其他解决方案,也可以(而且应该)这样做。

答案 5 :(得分:1)

abstract 关键字可能最适用于您的方案。请记住,如果您要提供一个您希望人们利用特定功能的程序集(dll),您可以通过接口实现黑匣子范例。如果提供要使用的接口并在内部实现接口,则程序集可以提供接口引用,而不会将对实例化类本身的引用移交给它。所有开发人员都知道您在接口定义中定义的属性和方法。这在插件架构中非常常见。

答案 6 :(得分:0)

好吧,我想我已经解开了这个谜语。您在两个部分部分中实现Foo_Impl。

一个是public partial class Foo_Impl {}实现,它基本上是一个什么都不做的空类,所以它在任何意义上都不可用。

另一个实现为private partial class Foo_Impl{}类的Foo:Foo_Impl成员。多数民众赞成你放置所有秘密功能,只能由Foo自己使用。任何类都可以从Foo_Impl的公共部分部分继承,但不能通过其私有Foo_Impl memeber使用仅对Foo可用的任何私有功能。示例class Voyeur:Foo_Impl用于说明每个正文都可以继承,但只有Foo可以实例化并使用它。 Voyeur无法窥视Foo_Impl以瞥见其有用的私有部分;)。

    using System;



    static class Program
    {


        static void Main(string[] args)
        {

            new Foo();
            new Voyeur();


            Console.ReadLine();

        }


    }


     public partial class Foo_Impl
    {
         // NOTHING in this public partial implementation. 
         // Visible but cannot be used for anything

        protected Foo_Impl()
        {
            Console.Write("public partial Foo_Impl's protected constructor ");
        }

    }



    public class Foo : Foo_Impl
    {
        private partial class Foo_Impl {

            // put all your secret implementations here that you dont want 
            // any other class to accidentally use (except Foo)

            public int fooAccessibleVariable=42;

            public Foo_Impl ()

            {
                Console.Write("private partial Foo_Impl contructor ");

            }



        }

        public Foo():base() // calls the public partial Foo_Impl's construtor
        {
            Console.WriteLine(" called from Foo()");


            Foo_Impl myFoo_Impl =  new Foo_Impl(); // calls the private partial Foo_Imp's constructor
            Console.WriteLine(" called from Foo()");

            Console.WriteLine("private Foo_Impl's variabe thats only accessible to Foo: {0}",
                myFoo_Impl.fooAccessibleVariable);
        }

    }

    class Voyeur:Foo_Impl
    {



        public Voyeur():base()// calls the public partial Foo_Impl's contructor 
        {

            Console.WriteLine("  called from Voyeur()");

        }


    }