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

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

3 голосов
/ 27 апреля 2011

В приведенном ниже коде я вызываю метод расширения [в среднем] 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;
    }
}

Ответы [ 4 ]

9 голосов
/ 27 апреля 2011

Нельзя приводить делегатов одного типа к другому в C #.Поскольку IEnumerable<T>.Average ожидает 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 голосов
/ 27 апреля 2011

Для метода расширения не определена перегрузка Среднее () , которое принимает аргумент типа 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);
0 голосов
/ 27 апреля 2011

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

public static int GetAge(Person p) 

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

myDelegate myDel = Person.GetAge;

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

double averageAge3 = Person.Average(myPerson.GetAge);
0 голосов
/ 27 апреля 2011

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

...