В другом месте на этой странице @Gusman предлагает хорошее решение, дистиллированное здесь:
abstract class AbstractBase<T>
{
public static String AbstractStaticProp { get; set; }
};
class Derived1 : AbstractBase<Derived1>
{
public static new String AbstractStaticProp
{
get => AbstractBase<Derived1>.AbstractStaticProp;
set => AbstractBase<Derived1>.AbstractStaticProp = value;
}
};
class Derived2 : AbstractBase<Derived2>
{
public static new String AbstractStaticProp
{
get => AbstractBase<Derived2>.AbstractStaticProp;
set => AbstractBase<Derived2>.AbstractStaticProp = value;
}
};
Однако есть несколько опасностей, на которые следует обратить внимание.
- В основном, нет ничего, что могло бы обеспечить или требовать, чтобы какой-либо / каждый данный производный класс действительно реализовывал (psudo-) "переопределенное" статическое свойство;
- С этим связано, поскольку не происходит унификация компилятораздесь корректные подписи (имя метода, арность параметров, типирование и т. д.) для «переопределенных» методов также не применяются.
- Хотя универсальный параметр предназначен , чтобы быть "
TSelf
"производного класса, в действительности T
является неограниченным и по существу произвольным.Это позволяет использовать два новых класса ошибок: если спецификация базового класса Y : AbstractBase<...>
ошибочно ссылается на другой класс X
, полученный из AbstractBase
, значения "абстрактного статического свойства" для X
и Y
будут неправильно сопоставленный - и / или - любой сайт вызова AbstractBase<T>.AbstractStaticProp
с аргументом ошибочного типа (таким как DateTime
) самопроизвольно - и молча - потребует нового нового "экземпляра"статическое свойство.
Последняя точка маркера может быть несколько смягчена добавлением ограничения на базовую базу:
/// v---- constraint added
abstract class AbstractBase<TSelf> where TSelf : AbstractBase<TSelf>
{
public static String AbstractStaticProp { get; set; }
};
Это исключает возможность class Derived2 : AbstractBase<DateTime> { /*...*/ }
, но неошибка class Derived2 : AbstractBase<Derived1> { /*...*/ }
.Это связано с повторяющейся загадкой, которая мешает всем попыткам ограничить универсальный тип какой-либо точной ветвью иерархии наследования типов:
"TSelf
problem "
Общие ограничения всегда зависят от предоставляемых аргументов типа, что, по-видимому, влечет за собой невозможность создания общего ограничения, которое гарантирует , что некоторые конкретные TArg
в пределахего область действия относится к типу, который является производным от самого себя, то есть определяемого непосредственного типа.
Ошибка в этом случае является примером этого;в то время как ограничение на AbstractBase<TSelf>
исключает несовместимые непересекающиеся типы, оно не может исключать непреднамеренное использование Derived2 : AbstractBase<Derived1>
.Что касается AbstractBase
, то предоставленный аргумент типа Derived1
точно удовлетворяет своему ограничению, независимо от того, какой из его подтипов получает (im-) должным образом.Я пытался все годами решать TSelf
;если кто-нибудь знает трюк, который я пропустил, пожалуйста, дайте мне знать!
В любом случае, есть еще пара моментов, о которых стоит упомянуть.Например, если вы не можете немедленно обнаружить проблему в следующем коде , вы должны согласиться с тем, что это немного опасно:
public static new String AbstractStaticProp
{
get => AbstractBase<Derived1>.AbstractStaticProp;
set => AbstractBase<Derived2>.AbstractStaticProp = value;
}
В идеале вы хотите получить компиляторчтобы сделать то, для чего он предназначен, а именно, понять, что все экземпляры свойств AbstractStaticProp
связаны между собой и, таким образом, каким-то образом обеспечить их объединение.Поскольку для статических методов это невозможно, единственная оставшаяся возможность - исключить лишние версии, эффективно сводя проблему к объединению всего лишь одной, очевидно, пустой операции.
Оказывается, исходный кодбыть слишком сложным;подход базового класса хочет, чтобы свернулся на более простом решении само по себе, без необходимости явно запрашивать его, например, эти свойства, отмеченные new
, похоже, делают с квалификацией в AbstractBase<Derived1>.AbstractStaticProp
".
Вы уже можете ссылаться на каждую соответствующую независимую копию статического свойства с помощью , квалифицируясь вместо имени производного класса (на самом деле, тестовая система @ Gusman показывает это), так что конечный результатзаключается в том, что объявления свойств в производном классе не нужны вообще. Без дальнейших церемоний, вот полная упрощенная версия:
abstract class AbstractBase<TSelf> where TSelf : AbstractBase<TSelf>
{
public static String AbstractStaticProp { get; set; }
};
class Derived1 : AbstractBase<Derived1> { };
class Derived2 : AbstractBase<Derived2> { };
Это работает идентично коду в верхней части.те же результаты, что и раньше.
static void Test()
{
Derived1.AbstractStaticProp = "I am Derived1";
Derived2.AbstractStaticProp = "I am Derived2";
Debug.Print(Derived1.AbstractStaticProp); // --> I am Derived1
Debug.Print(Derived2.AbstractStaticProp); // --> I am Derived2
}