Скрытие унаследованных членов
Я ищу способ эффективного скрыть унаследованные элементы. У меня есть библиотека классов, которые наследуются от общих базовых классов. Некоторые из более поздних классов потомков наследуют свойства зависимостей, которые стали рудиментарными и могут немного запутываться при использовании IntelliSense или использования классов в визуальном дизайнере.
Эти классы - все элементы управления, которые записываются для компиляции для WPF или Silverlight 2.0. Я знаю о ICustomTypeDescriptor
и ICustomPropertyProvider
, но я уверен, что они не могут использоваться в Silverlight.
Это не такая функциональная проблема, как проблема юзабилити. Что мне делать?
Обновление
Некоторые из свойств, которые я действительно хотел бы скрыть, происходят от предков, которые не являются моими собственными, и из-за конкретного инструмента, для которого я проектирую, я не могу спрятать член с оператором new
. (Я знаю, это смешно)
Ответы
Ответ 1
Переопределите их, как Майкл Предлагает выше, и чтобы люди не использовали переопределенные методы (sp?), отмечайте их как устаревшие:
[Obsolete("These are not supported in this class.", true)]
public override void dontcallmeanymore()
{
}
Если для второго parm установлено значение true, ошибка компилятора будет сгенерирована, если кто-либо попытается вызвать этот метод, а строка в первом parm - это сообщение. Если parm2 является ложным, генерируется только предупреждение компилятора.
Ответ 2
Пока вы не можете предотвратить использование этих унаследованных членов, я должен скрыть их от IntelliSense с помощью EditorBrowsableAttribute:
Using System.ComponentModel;
[EditorBrowsable(EditorBrowsableState.Never)]
private string MyHiddenString = "Muahahahahahahahaha";
Изменить: просто увидел это в комментариях к документации, что делает его бесполезным для этой цели:
Существует заметное примечание, в котором говорится, что этот атрибут "не подавляет членов из класса в той же сборке". Это верно, но не полностью. На самом деле атрибут не подавляет членов класса из одного и того же решения.
Ответ 3
Одна потенциальная вещь, которую вы можете сделать, состоит в том, чтобы содержать объект, а не расширяться от другого класса. Это даст вам максимальную гибкость с точки зрения раскрытия того, что вы хотите разоблачить, но если вам абсолютно необходим объект такого типа, это не идеальное решение (однако вы можете выставить объект из геттера).
Таким образом:
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;
}
}
}
Ответ 4
Я думаю, что вы лучший наименее хакерский способ - рассмотреть композицию, а не наследование.
Или вы можете создать интерфейс, в котором есть члены, которые вы хотите, и ваш производный класс реализует этот интерфейс и программирует интерфейс.
Ответ 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 внутренним и только выставить мой унаследованный класс.
Ответ 6
Чтобы полностью скрыть и отметить не использовать, в том числе intellisense, который, я считаю, является тем, что ожидают большинство читателей...
[Obsolete("Not applicable in this class.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Ответ 7
Я тестировал все предлагаемые решения, и они действительно не скрывают новых членов.
Но это одно:
[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; }
}
Ответ 8
Вы можете использовать интерфейс
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();
}
}
Ответ 9
Несмотря на то, что ясно сказано выше, что в С# невозможно изменить модификаторы доступа для унаследованных методов и свойств, я преодолел эту проблему с помощью своего рода "поддельного наследования" с помощью неявного приведения.
Пример:
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
.