Nano Hash - криптовалюты, майнинг, программирование

Удаление метода экземпляра в .NET

Можно ли создать делегат метода экземпляра, не указывая экземпляр во время создания? Другими словами, можете ли вы создать «статический» делегат, который принимает в качестве первого параметра экземпляр, для которого должен быть вызван метод?

Например, как я могу создать следующий делегат, используя отражение?

Func<int, string> = i=>i.ToString();

Я знаю, что могу использовать methodInfo.Invoke, но это медленнее и не проверяет правильность типа, пока не будет вызвано.

Когда у вас есть MethodInfo определенного статического метода, можно создать делегат с помощью Delegate.CreateDelegate(delegateType, methodInfo), и все параметры статического метода останутся свободными.

Как указал Джон Скит, вы можете просто применить то же самое, чтобы сделать открытый делегат метода экземпляра, если метод не является виртуальным для ссылочного типа. Решить, какой метод вызывать для виртуального метода, сложно, так что это не так уж и тривиально, а типы значений выглядят так, как будто они вообще не работают.

Для типов значений CreateDelegate ведет себя очень странно:

var func37 = (Func<CultureInfo,string>)(37.ToString);
var toStringMethod = typeof(int).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(CultureInfo) }, null);
var func42 = (Func<CultureInfo,string>)Delegate.CreateDelegate(typeof(Func<CultureInfo,string>), 42, toStringMethod,true);
Console.WriteLine( object.ReferenceEquals(func37.Method,func42.Method)); //true
Console.WriteLine(func37.Target);//37
Console.WriteLine(func42.Target);//42
Console.WriteLine(func37(CultureInfo.InvariantCulture));//37
Console.WriteLine(func42(CultureInfo.InvariantCulture));//-201040128... WTF?

Вызов CreateDelegate с null в качестве целевого объекта вызывает исключение привязки, если метод экземпляра принадлежит типу значения (это работает для ссылочных типов).

Несколько лет спустя: Неправильно привязанная цель, из-за которой func42(CultureInfo.InvariantCulture); возвращала "-201040128" вместо "42" в моем примере, заключалась в повреждении памяти, которое могло сделать возможным удаленное выполнение кода (cve-2010-1898); это было исправлено в 2010 году в ms10-060 обновление безопасности. Текущие фреймворки правильно печатают 42! Это не облегчает ответ на этот вопрос, но объясняет особенно странное поведение в примере.


Ответы:


1

На самом деле вы выбрали особенно сложный пример по двум причинам:

  • ToString() — это виртуальный метод, унаследованный от object, но переопределенный в Int32.
  • int — это тип значения, и существуют странные правила с Delegate.CreateDelegate(), когда дело доходит до типов значений и методов экземпляра — в основном первым эффективным параметром становится ref int, а не int.

Однако вот пример для String.ToUpper, у которого нет ни одной из этих проблем:

using System;
using System.Reflection;

class Test
{
    static void Main()
    {
        MethodInfo method = typeof(string).GetMethod
            ("ToUpper", BindingFlags.Instance | BindingFlags.Public,
             null, new Type[]{}, null);

        Func<string, string> func = (Func<string, string>)
            Delegate.CreateDelegate(typeof(Func<string, string>),
                                    null,
                                    method);

        string x = func("hello");

        Console.WriteLine(x);
    }
}

Если этого достаточно для вас, отлично... если вы действительно хотите int.ToString, мне придется немного постараться :)

Вот пример для типа значения с использованием нового типа делегата, который принимает свой первый параметр по ссылке:

using System;
using System.Reflection;

public struct Foo
{
    readonly string value;

    public Foo(string value)
    {
        this.value = value;
    }

    public string DemoMethod()
    {
        return value;
    }
}

class Test
{
    delegate TResult RefFunc<TArg, TResult>(ref TArg arg);

    static void Main()
    {
        MethodInfo method = typeof(Foo).GetMethod
            ("DemoMethod", BindingFlags.Instance | BindingFlags.Public,
             null, new Type[]{}, null);
        RefFunc<Foo, string> func = (RefFunc<Foo, string>)
            Delegate.CreateDelegate(typeof(RefFunc<Foo, string>),
                                    null,
                                    method);

        Foo y = new Foo("hello");
        string x = func(ref y);

        Console.WriteLine(x);
    }
}
31.07.2009
  • Это один из тех случаев, когда становится ясно, что C# еще есть куда расти как функциональному языку. Отношение к функциям как к гражданам первого класса по-прежнему не так гладко, как хотелось бы. Есть ли способ использовать динамические функции в C # 4, чтобы упростить такие вещи? 31.07.2009
  • @LBushkin: я так не думаю. На самом деле, динамическая типизация и лямбда-выражения не очень хорошо сочетаются друг с другом — компилятор должен знать, в какой тип преобразовать лямбда-выражение во время компиляции. 31.07.2009
  • Я был тестировал int.ToString, но для реального использования, я полагаю, я мог бы обойтись без виртуальных методов, хотя и не без структур. В любом случае, спасибо за предупреждение, я упустил из виду сложность виртуальных методов, и сообщение об ошибке не совсем информативно... 31.07.2009
  • Вау, ты быстрый ;-). Я не уверен, что это возможно - я попытался просмотреть MethodInfo.Invoke с рефлектором, чтобы увидеть, обрабатывает ли он структуры специально, но он оказывается в собственном коде (без сомнения, какая-то заглушка CLR). 31.07.2009
  • Похоже, int — это особый случай? Код, подобный вашему, не работает для int. Странный. Не то, чтобы невозможно было найти обходные пути для встроенных типов, но это странно. 31.07.2009
  • @Eamon: Какой метод вы пытались использовать? Я подозреваю, что это должен быть не виртуальный метод экземпляра. Все как-то усложняется... 31.07.2009
  • Есть ли страница, на которой задокументирован ваш второй пункт (странные правила)? Я не могу найти места, где тот факт, что первый параметр передается по ссылке, кроме этого ответа. 19.12.2017
  • @Lazlo: я не уверен - боюсь, я сам не помню подробностей. 20.12.2017
  • 10 лет спустя... Я предполагаю, что язык все еще отсутствует в этой области? Мне нужно связать делегата с получателем свойств для типа структуры, неизвестного во время компиляции. :( Это вообще возможно? 24.09.2019
  • @JamesHoux: возможно некоторые из ref изменений в C# 7 помогут, но я не уверен. Возможно, лучше задать новый вопрос с вашим конкретным примером и тем, что вы пробовали. 24.09.2019
  • @JonSkeet На самом деле ... похоже, я совсем запутался. Я думал, что ваше предложение RefFunc‹› было строго гипотетическим. На самом деле вы говорили, что RefFunc реален! Я попробовал RefFunc‹›, и компилятор сказал мне, что это неизвестный тип! Ctrl+. не показывает никаких исправлений. Как я могу использовать RefFunc‹›? 25.09.2019
  • @JamesHoux: это тип делегата, объявленный в классе Test. 25.09.2019
  • @JonSkeet О боже. Я полностью пропустил это. Я думаю, что у меня развивается кодовая слепота или что-то в этом роде. ЛОЛ Спасибо за ответ! 25.09.2019

  • 2

    Я не уверен, но может быть Открытые делегаты могут помочь тебе.

    Обновление: перейдите по этой ссылке, если первая не работает.

    31.07.2009
  • Эта ссылка ведет на страницу 404 — может быть, вы опечатались? 31.07.2009
  • Странно, щелчок по ссылке приводит меня к странице 404, обновление страницы вызывает ту же ошибку 404. Но нажатие ввода в адресной строке затем (т.е. удаление реферера) открывает страницу. По-видимому, какая-то ошибка браузера/сервера (только FF3.5.1 - хром работает нормально). Во всяком случае, нашел страницу теперь ;-) 31.07.2009
  • Спасибо, Эймон. Я впервые вижу эту ошибку, обычно ссылки на блоггеры работают нормально. 01.08.2009

  • 3

    Вы можете использовать Lambdas, чтобы получить "несколько" скомпилированную статическую оболочку для вашего метода экземпляра.

    Приведенный ниже пример не совсем молниеносно быстр, но он должен быть значительно быстрее, чем любой простой динамический вызов.

    Выход

    100000 iterations took 4 ms 
    1000000 iterations took 18 ms 
    10000000 iterations took 184 ms
    

    Код

    class Program
    {
    
       public sealed class Test
       {
          public String Data { get; set; }
          public override string ToString()
          {
             return Data;
          }
       }
    
       static void Main(string[] args)
       {
          TestRun(100000);
          TestRun(1000000);
          TestRun(10000000);
       }
    
       private static void TestRun(int iterations)
       {
          var toString = typeof(Test).GetMethod("ToString",
                                                BindingFlags.Instance
                                                | BindingFlags.Public,
                                                null,
                                                Type.EmptyTypes,
                                                null);
          var call = GetCall<Test, String>(toString);
          var tests
             = (from i in Enumerable.Range(1, iterations)
                select new Test { Data = "..." + i }).ToList();
    
          var sw = Stopwatch.StartNew();
          tests.ForEach(i => call(i));
          sw.Stop();
          Console.WriteLine("{0} iterations took {1} ms", iterations, sw.ElapsedMilliseconds);
       }
    
       private static Func<T, M> GetCall<T, M>(MethodInfo methodInfo)
       {
          var input = Expression.Parameter(typeof(T), "input");
          MethodCallExpression member = Expression.Call(input, methodInfo);
          var lambda = Expression.Lambda<Func<T, M>>(member, input);
    
          return lambda.Compile();
       }
    }
    
    31.07.2009

    4

    Гуг, возможно, может использовать «динамический» тип в .NET 4.0. Однако делегату нужен экземпляр (для нестатических методов). Проблемы более сложны, чем локки в первый раз из-за полиморфизма и т. Д.

    31.07.2009
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..