Есть ли способ ограничить функции в классе C # использованием только определенной подписи?

#c# #compile-time

#c# #время компиляции

Вопрос:

Я пишу класс, который в идеале должен иметь несколько методов с одной и той же подписью. Есть ли способ заставить класс проверять свои методы, если все они следуют одной и той же подписи?

Было бы идеально, если бы проверку можно было выполнить во время компиляции / во время сборки

Если вы предполагаете, что подпись будет int <methodName>(string, int, char)

 public class Conditions {
        // no error
        int MethodA(string a, int b, char c)
        {
            return 0;
        }
        // no error
        int MethodB(string a, int b, char c)
        {
            return 1;
        }

        // should throw error because return type does not match signature
        string MethodC(string a, int b, char c)
        {
            return "Should throw an error for this function";
        }
    }
}
  

Комментарии:

1. Почему вы хотите, чтобы она выдавалась, почему бы не использовать ее во время компиляции, в таком случае просто используйте контракт как интерфейс.

2. Зачем вам это нужно? Это очень необычные запросы, которые предполагают, что реальная проблема заключается в чем-то другом.

3. @PanagiotisKanavos Мне нужно было сделать это раньше…

4. @MarcGravell по какой причине? Я могу понять реализацию команд или что-то подобное, что может быть реализовано с использованием интерфейсов. Компилятор не может заставить все методы иметь одинаковую подпись, хотя

5. Я бы никогда не хотел противоречить @MarcGravell, однако » Я бы хотел, чтобы подписи методов были стандартизированы, чтобы я мог проще вызывать их код » звучит как задание для интерфейса

Ответ №1:

Это своего рода обман, но если вы требуете, чтобы разработчик зарегистрировал свои методы, вы можете вызвать ошибку во время компиляции, потребовав, чтобы метод соответствовал делегату.

По сути, именно так работают обработчики событий и обратные вызовы.

 namespace Framework
{
    public delegate int MyApiSignature(int a, string b, char c);

    public class Core
    {
        static public void RegisterMethod(MyApiSignature method) 
        {
            //Doesn't even have to actually do anything
        }
    }
}


namespace Custom
{
    using Framework;

    class Foo
    {
        public Foo()
        {
            Core.RegisterMethod(MethodA);  //Works
            Core.RegisterMethod(MethodB);  //Compile-time error
        }

        public int MethodA(int a, string b, char c)
        {
            return 0;
        }

        public int MethodB(int a, string b, byte c)
        {
            return 0;
        }
    }
}
  

Ответ №2:

Вы могли бы выполнить модульный тест:

     [TestMethod]
    public void Conditions_MethodsHaveCorrectSignature()
    {
        var whitelist = new List<string> { "Finalize", "MemberwiseClone" };
        var t = typeof(Conditions);
        var m = t.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);

        foreach (var item in m.Where(x => !whitelist.Contains(x.Name)))
        {
            Assert.AreEqual(typeof(int), item.ReturnType);

            CollectionAssert.AreEquivalent(new List<Type> { typeof(string), typeof(int), typeof(char) },
                item.GetParameters().Select(x => x.ParameterType).ToList());
        }
    }
  

Ответ №3:

Не напрямую. Вы могли бы написать анализатор для этого, используя Roslyn, или вы могли бы написать модульный тест, который проверяет подписи с помощью отражения.

Комментарии:

1. Или быстрый и грязный способ — назначить все методы для Func<string,int,char,string> . Это вызовет недовольство компилятора. Хотя и очень грязно

2. @PanagiotisKanavos, однако, не ужасно — особенно если вы активируете их с помощью отражения… Dictionary<string, Func<string,int,char,string>> становится очень заманчивым

3. Функциональная композиция F # по существу работает путем объединения в цепочки Func<> , так что, возможно, это не такое уж грязное решение