C # вопрос о делегате

#c# #delegates #average

#c# #делегаты #среднее

Вопрос:

В приведенном ниже коде я вызываю метод расширения [в среднем] 3 раза. Сначала с помощью лямбда-выражения, затем анонимным методом и, в-третьих, с помощью делегата.

Способ с делегатом не работает. Кто-нибудь может объяснить мне, почему?

Большое спасибо заранее!

————КОД————-

 using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        List<Person> people = new List<Person> {
            new Person { FirstName = "William", LastName = "Schokkele", Age = 35 },
            new Person { FirstName = "Bill",    LastName = "Gates", Age = 50 },
            new Person { FirstName = "Hanne",   LastName = "Schokkele", Age = 7 }
        };

        Person myPerson = new Person { FirstName = "Hanne", LastName = "Schokkele", Age = 7 };
        myDelegate myDel = new myDelegate(myPerson.GetAge);

        double averageAge= people.Average(p => p.Age);
        double averageAge2 = people.Average(delegate(Person p) { return p.Age; });
        double averageAge3 = people.Average(myDel); //THIS LINE DOESNT WORK. ERROR MESSAGE LIST<Person> does not contains a definition for 'average'
    }
}

public delegate int myDelegate(Person p);

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public int GetAge(Person p)
    {
        return p.Age;
    }
}
  

Ответ №1:

Вы не можете преобразовать делегат одного типа в другой в C #. Поскольку IEnumerable<T>.Average ожидается a Func<T, int> , это тот делегат, который вам нужно предоставить, независимо от того факта, что он имеет ту же подпись, что и myDelegate .

Вы можете либо изменить это:

 myDelegate myDel = new myDelegate(myPerson.GetAge);
  

в:

 Func<Person, int> myDel = new Func<Person, int>(myPerson.GetAge);
  

или просто передайте метод в качестве параметра, который создаст для вас соответствующий делегат:

 double averageAge3 = people.Average(myPerson.GetAge);  // this will work
  

Обратите внимание, что создание GetAge метода экземпляра (вместо статического метода) не имеет смысла, поскольку он возвращает возраст объекта, переданного в качестве параметра, вместо родительского ( this ) объекта.

Это становится очевидным, когда вы смотрите на myPerson объект, экземпляр которого создается с «фиктивными» значениями, просто чтобы получить доступ к его GetAge методу, который не использует ни одно из его свойств (вы могли бы просто написать double averageAge3 = people.Average(new Person().GetAge); , и ничего бы не изменилось).

Я бы рекомендовал либо использовать анонимный метод (предпочтительный), либо создать свой GetAge метод static .

Ответ №2:

Для метода расширения не определена перегрузка Average(), который принимает аргумент типа myDelegate .

Тот, который соответствует вашему случаю, следующий :

 public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector);
  

Вы должны использовать Func<T,TReturn> , чтобы заставить его работать вместо myDelegate .

Пример:

 Person myPerson = new Person { FirstName = "Hanne", LastName = "Schokkele", Age = 7 };
Func<Person, Int32> myDel = myPerson.GetAge;

double averageAge = people.Average(p => p.Age);
double averageAge2 = people.Average(delegate(Person p) { return p.Age; });
double averageAge3 = people.Average(myDel);
  

Ответ №3:

Вы можете сделать double averageAge3 = myDel.Invoke(myPerson); или как именно вы хотите, чтобы это выглядело?

Ответ №4:

вы должны пометить свой делегат статическим (у него нет контекста экземпляра)

 public static int GetAge(Person p) 
  

затем определите

 myDelegate myDel = Person.GetAge;
  

или вызвать напрямую

 double averageAge3 = Person.Average(myPerson.GetAge);