隐藏继承成员

51

我正在寻找一种有效的方法来隐藏继承成员。 我有一个类库,其中包含从共同基类继承的类。 一些较新的子类继承了已经变得陈旧无用的依赖属性,在使用 IntelliSense 或在视觉设计器中使用这些类时会有点令人困惑。

这些类都是控件,编写时要为 WPF 或 Silverlight 2.0 编译。 我知道 ICustomTypeDescriptorICustomPropertyProvider,但我非常确定它们不能在 Silverlight 中使用。

这不是一个功能问题,而是一个可用性问题。 我该怎么办?

更新

其中一些我真正想隐藏的属性来自于不属于我自己的祖先,由于我正在设计的特定工具,我不能使用 new 运算符进行成员隐藏。(我知道,这很荒谬)

9个回答

39

按照Michael的建议覆盖它们上面,并将被覆盖的方法标记为已过时,以防止人们使用它们。

[Obsolete("These are not supported in this class.", true)]
public override  void dontcallmeanymore()
{
}

如果第二个参数设置为true,则在任何人尝试调用该方法并且第一个参数中的字符串是消息时,将生成编译器错误。 如果parm2为false,则仅会生成编译器警告。


你不能使用相同的效果进行封装吗? - user1618054
@JamesM,使用true可以防止成员_access_过时,使用sealed可以防止成员_inheritance_。因此,密封类不能派生自其他类,而密封方法允许其他类成员被覆盖,但是防止该成员被覆盖。密封不会阻止调用该类或成员,而如果您尝试调用已过时的true,则会抛出编译器错误。 - Robert Petz
@RobertPetz 当使用过时的内容时,会发出警告而不是错误。这是一个很大的区别。 - user1618054
1
@JamesM 嗯...你有没有看过你第一次评论的答案? - Robert Petz
@RobertPetz 你显然没有读懂我的话,不过无聊的讽刺还是值得称赞的。 - user1618054
2
@JamesM 哈哈,我确实这样做了,这就是为什么我回答你的原始问题并指出密封未执行相同任务的原因。然后你指出Obselete只会发出警告,这是不正确的,因为true作为第二个参数会导致编译器错误而不是警告 - 正如你直接评论的答案所述。如果我误解了你,我欢迎澄清。这是MSDN来源 - Robert Petz

20

虽然据我所知你无法阻止那些继承成员的使用,但你应该能够使用 EditorBrowsableAttribute 从 IntelliSense 中隐藏它们:

Using System.ComponentModel;

[EditorBrowsable(EditorBrowsableState.Never)]
private string MyHiddenString = "Muahahahahahahahaha";

编辑:刚刚在文档注释中看到这个,对于这个目的来说它有点无用:

有一个重要的提示说明这个属性“不会隐藏同一程序集中类的成员”。这是正确的,但不完整。实际上,该属性不会隐藏同一解决方案中类的成员。


14

你可以尝试包含对象而不是从其他类扩展,这将为您提供最大的灵活性,以公开您想要公开的内容,但如果您绝对需要对象是该类型,则不是理想的解决方案(但您可以通过getter公开对象)。

因此:

public class MyClass : BaseClass
{
    // Your stuff here
}

变成:

public class MyClass
{
    private BaseClass baseClass;

    public void ExposeThisMethod()
    {
        baseClass.ExposeThisMethod();
    }
}

或者:

public class MyClass
{
    private BaseClass baseClass;

    public BaseClass BaseClass
    {
        get
        {
            return baseClass;
        }
    }
}

8

我认为最好的做法是考虑组合而不是继承,这样可以避免过度使用hackish方式。

或者,您可以创建一个具有所需成员的接口,让派生类实现该接口,并针对接口进行编程。


5
我知道这个问题已经有了几个答案,而且现在很老了,但是最简单的方法就是将它们声明为new private
考虑一个我正在处理的示例,其中我有一个API,可以使用第三方DLL中的每个方法。我必须使用它们的方法,但我想使用.Net属性,而不是“getThisValue”和“setThisValue”方法。因此,我建立了第二个类,继承了第一个类,创建了一个使用get和set方法的属性,然后将原始的get和set方法覆盖为私有。它们仍然可供任何想要在其上构建不同内容的人使用,但如果他们只想使用我正在构建的引擎,则可以使用属性而不是方法。
使用双类方法消除了无法使用new声明隐藏成员的任何限制。如果成员被标记为虚拟的,则不能使用override
public class APIClass
{
    private static const string DllName = "external.dll";

    [DllImport(DllName)]
    public extern unsafe uint external_setSomething(int x, uint y);

    [DllImport(DllName)]
    public extern unsafe uint external_getSomething(int x, uint* y);

    public enum valueEnum
    {
        On = 0x01000000;
        Off = 0x00000000;
        OnWithOptions = 0x01010000;
        OffWithOptions = 0x00010000;
    }
}

public class APIUsageClass : APIClass
{
    public int Identifier;
    private APIClass m_internalInstance = new APIClass();

    public valueEnum Something
    {
        get
        {
            unsafe
            {
                valueEnum y;
                fixed (valueEnum* yPtr = &y)
                {
                    m_internalInstance.external_getSomething(Identifier, yPtr);
                }
                return y;
            }
        }
        set
        {
            m_internalInstance.external_setSomething(Identifier, value);
        }
    }

    new private uint external_setSomething(int x, float y) { return 0; }
    new private unsafe uint external_getSomething(int x, float* y) { return 0; }
}

现在valueEnum对于这两个类都是可用的,但只有属性在APIUsageClass类中可见。APIClass仍然可以供那些想要扩展原始API或以不同方式使用它的人使用,而APIUsageClass则适用于那些想要更简单东西的人。
最终,我将使APIClass成为内部类,只公开我的继承类。

如何在依赖属性中使用它? - user1618054

5
为了完全隐藏并标记为不使用,包括智能感知,我相信大多数读者都希望这样做。
[Obsolete("Not applicable in this class.")] 
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]

1

我测试了所有提出的解决方案,它们并不能真正隐藏新成员。

但是这个可以:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new string MyHiddenProperty
{ 
    get { return _myHiddenProperty; }
}

但在代码后台仍然可以访问,因此也要添加过时的属性

[Obsolete("This property is not supported in this class", true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new string MyHiddenProperty
{ 
    get { return _myHiddenProperty; }
}

1

虽然上面明确说明在C#中无法更改继承方法和属性的访问修饰符,但我通过一种类似于使用隐式转换的“假继承”方式解决了这个问题。

示例:

public class A
{
      int var1;
      int var2;

      public A(int var1, int var2)
      {
            this.var1 = var1;
            this.var2 = var2;
      }
      public void Method1(int i)
      {
            var1 = i;
      }
      public int Method2()
      {
            return var1+var2;
      }
}

现在假设你想让一个名为 class B 的类继承自 class A,但希望改变一些可访问性甚至完全更改 Method1
public class B
{
      private A parent;

      public B(int var1, int var2)
      {
            parent = new A(var1, var2);
      } 

      int var1 
      {
            get {return this.parent.var1;}
      }
      int var2 
      {
            get {return this.parent.var2;}
            set {this.parent.var2 = value;}
      }

      public Method1(int i)
      {
            this.parent.Method1(i*i);
      }
      private Method2()
      {
            this.parent.Method2();
      }


      public static implicit operator A(B b)
      {
            return b.parent;
      }
}

通过在结尾处包含隐式转换,它允许我们在需要时将B对象视为A。还可以有用地从A->B定义隐式转换。

这种方法最大的缺陷是您需要重新编写每个要“继承”的方法/属性。 可能甚至还有更多缺陷,但我喜欢将其用作一种“虚拟继承”。

注意:

虽然这允许更改public属性的可访问性,但它并未解决使protected属性公开的问题。


0

你可以使用一个接口

    public static void Main()
    {
        NoRemoveList<string> testList = ListFactory<string>.NewList();

        testList.Add(" this is ok ");

        // not ok
        //testList.RemoveAt(0);
    }

    public interface NoRemoveList<T>
    {
        T this[int index] { get; }
        int Count { get; }
        void Add(T item);
    }

    public class ListFactory<T>
    {
        private class HiddenList: List<T>, NoRemoveList<T>
        {
            // no access outside
        }

        public static NoRemoveList<T> NewList()
        {
            return new HiddenList();
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接