С#, доступ к статическому свойству типа T в общем классе
Я пытаюсь выполнить следующий сценарий, что общий TestClassWrapper сможет получить доступ к статическим свойствам классов, из которых он сделан (все они будут получены из TestClass). Что-то вроде:
public class TestClass
{
public static int x = 5;
}
public class TestClassWrapper<T> where T : TestClass
{
public int test()
{
return T.x;
}
}
Дает ошибку: 'T' is a 'type parameter', which is not valid in the given context.
Любые предложения?
Ответы
Ответ 1
Вы не можете, в основном, по крайней мере, не без размышлений.
Один из вариантов заключается в том, чтобы поставить делегата в свой конструктор, чтобы тот, кто создает экземпляр, мог указать, как его получить:
var wrapper = new TestClassWrapper<TestClass>(() => TestClass.x);
Вы можете сделать это с отражением, если необходимо:
public class TestClassWrapper<T> where T : TestClass
{
private static readonly FieldInfo field = typeof(T).GetField("x");
public int test()
{
return (int) field.GetValue(null);
}
}
(При необходимости добавьте соответствующие флаги привязки).
Это не очень хорошо, но по крайней мере вам нужно всего лишь разглядеть поле...
Ответ 2
Конечно, вы можете просто написать это:
public int test()
{
return TestClass.x;
}
Даже в нетривиальном примере вы не можете переопределить статическое поле, поэтому всегда вызываете его из вашего базового класса.
Ответ 3
Почему бы просто не вернуть TestClass.x
?
Ответ 4
В этой ситуации вы предполагаете, что T является подклассом TestClass. Подклассы TestClass не будут иметь статический int x.
Ответ 5
Generics не поддерживает ничего, связанное со статическими членами, так что это не сработает. Мой совет: не делайте его статичным. Предполагая, что поле действительно относится к конкретному T
, вы также можете использовать отражение:
return (int) typeof(T).GetField("x").GetValue(null);
но я не рекомендую его.
Ответ 6
T - это тип, а не параметр или переменная, поэтому вы не можете выбрать значение любого из любых членов. Вот пример кода.
public class UrlRecordService
{
public virtual void SaveSlug<T>(T entity) where T : ISlugSupport
{
if (entity == null)
throw new ArgumentNullException("entity");
int entityId = entity.Id;
string entityName = typeof(T).Name;
}
}
public interface ISlugSupport
{
int Id { get; set; }
}
Ответ 7
Другим решением является просто не сделать его статическим и работать с ограничением new()
на T, чтобы создать экземпляр объекта. Затем вы можете работать с интерфейсом, и оболочка может получить свойство из любого класса, который реализует этот интерфейс:
public interface XExposer
{
Int32 X { get; }
}
public class TestClass : XExposer
{
public Int32 X { get { return 5;} }
}
public class XExposerWrapper<T> where T : XExposer, new()
{
public Int32 X
{
get { return new T().X; }
}
}
Фактически вы можете изменить это на public static Int32 X
на TestClassWrapper и просто получить его как Int32 fetchedX = XExposerWrapper<TestClass>.X;
Хотя, поскольку любые вызовы кода должны будут давать параметр T тем же самым ограничениям, класс-оболочка на данный момент довольно ненужен, так как сам этот код вызова также может просто выполнить new T().X
и не беспокоить оболочку.
Тем не менее, есть некоторые интересные модели наследования, где такая структура полезна. Например, abstract class SuperClass<T> where T : SuperClass<T>, new()
может создавать и возвращать тип T в своих статических функциях, эффективно позволяя создавать наследуемые статические функции, которые адаптируются к дочерним классам (которые затем должны определяться как class ChildClass : SuperClass<ChildClass>
). Определяя protected abstract
функции/свойства суперкласса, вы можете создавать функции, которые применяют одну и ту же логику к любому наследуемому объекту, но настраиваются под этот подкласс в соответствии с его реализацией этих тезисов. Я использую это для классов базы данных, где запрос имени таблицы и выборки реализуется дочерним классом. Поскольку свойства защищены, они также не подвергаются воздействию.