Доступ к абстрактному статическому члену интерфейса

Как мне получить реализованный из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;
}
Другие вопросы по тегам