Доступ к абстрактному статическому члену интерфейса
Как мне получить реализованный изFooImpl
либо из интерфейса, либо из экземпляра объекта?
public interface IFoo {
static abstract string Name { get; }
}
public class FooImpl : IFoo {
public static string Name => "Foo";
}
Кажется, я не могу получить значение от этих методов. Я что-то пропустил? Обратите внимание, что я хотел бы получить доступ кName
от любой реализацииIFoo
не зная используемой реализации. Как таковой,FooImpl.Name
будет компилироваться и запускаться, но это не то, что я хочу сделать напрямую.
IFoo anImpl = new FooImpl();
Console.WriteLine(anImpl.Name); // [CS0176] Member 'IFoo.Name' cannot be accessed with an instance reference; qualify it with a type name instead
// I know this won't work
Console.WriteLine(IFoo.Name); // [CS8926] A static virtual or abstract interface member can be accessed only on a type parameter.
static string GetName<T>() where T : IFoo
{
return T.Name;
}
// I know this won't work
Console.WriteLine(GetName<IFoo>()); // [CS8920] The interface 'IFoo' cannot be used as type argument. Static member 'IFoo.Name.get' does not have a most specific implementation in the interface.
static string GetName2<T>(T implementation) where T : IFoo
{
return T.Name;
}
Console.WriteLine(GetName2(anImpl)); // [CS8920] The interface 'IFoo' cannot be used as type argument. Static member 'IFoo.Name.get' does not have a most specific implementation in the interface.
Я не уверен, что еще попробовать. Этот пост связан, но я не вижу ответа, кроме размышлений.
2 ответа
Обратите внимание, что я хотел бы получить доступ к
Name
от любой реализацииIFoo
не зная используемой реализации
Да, это невозможно (кроме отражения). Члены статического абстрактного интерфейса, как и любые другие статические члены, принадлежат типу, а не экземпляру, поэтому их можно получить только через тип. Вы можете использовать решение из связанного ответа или позвонить по телефонуGetName
"динамически", т.е. что-то вроде:
var fooName = typeof(...).GetMethod(nameof(....GetName))
.MakeGenericMethod(anImpl.GetType())
.Invoke(null, null);
Также вы можете обернуть любой из подходов в какой-нибудь помощник и сохранить значения обработанных типов вDictionary<Type, string>
(илиConcurrentDictionary
), чтобы уменьшить стоимость отражения. Или даже предварительно создать словарь (зависит от варианта использования).
Есть и другой подход, но он требует некоторых изменений в иерархии типов. Вы можете использовать любопытно повторяющийся шаблон шаблона (CRTP) + дополнительный метод + дополнительный общий интерфейс с реализацией метода по умолчанию:
public interface IFoo {
static abstract string Name { get; }
public string GetName();
}
public interface IFoo<T> : IFoo where T : IFoo<T> // CRTP
{
string IFoo.GetName() => T.Name;
}
public class FooImpl : IFoo<FooImpl> {
public static string Name => "Foo";
}
И использование:
IFoo anImpl = new FooImpl();
Console.WriteLine(anImpl.GetName());
Вы реализуете его с помощью свойства, доступного только для чтения, и присваиваете ему значение по умолчанию.
public interface IFoo {
static abstract string Name { get; }
}
public class FooImpl : IFoo {
public static string Name {get; } = "Foo";
}
Затем вы читаете значение, где потребляется как
public void DoStuff<T>() where T : IFoo
{
string name = T.Name;
}
или конкретная реализация
public void DoStuff()
{
string name = FooImpl.Name;
}