Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tyto příklady ukazují, jak používat kovarianci a kontravarianci v obecných delegátech Func a Action, abyste umožnili opakované použití metod a zajistili větší flexibilitu v kódu.
Další informace o kovarianci a kontravarianci naleznete v části Variance v delegátech (C#).
Použití delegátů s parametry kovariantního typu
Následující příklad ilustruje výhody podpory kovariance v obecných Func delegátech. Metoda FindByTitle přebírá parametr String typu a vrací objekt typu Employee . Tuto metodu však můžete přiřadit delegátu Func<String, Person> , protože Employee dědí Person.
// Simple hierarchy of classes.
public class Person { }
public class Employee : Person { }
class Program
{
static Employee FindByTitle(String title)
{
// This is a stub for a method that returns
// an employee that has the specified title.
return new Employee();
}
static void Test()
{
// Create an instance of the delegate without using variance.
Func<String, Employee> findEmployee = FindByTitle;
// The delegate expects a method to return Person,
// but you can assign it a method that returns Employee.
Func<String, Person> findPerson = FindByTitle;
// You can also assign a delegate
// that returns a more derived type
// to a delegate that returns a less derived type.
findPerson = findEmployee;
}
}
Použití delegátů s parametry kontravariantního typu
Následující příklad ukazuje výhody podpory kontravariance u obecných Action delegátů. Metoda AddToContacts přebírá parametr typu Person . Tuto metodu však můžete přiřadit delegátu Action<Employee> , protože Employee dědí Person.
public class Person { }
public class Employee : Person { }
class Program
{
static void AddToContacts(Person person)
{
// This method adds a Person object
// to a contact list.
}
static void Test()
{
// Create an instance of the delegate without using variance.
Action<Person> addPersonToContacts = AddToContacts;
// The Action delegate expects
// a method that has an Employee parameter,
// but you can assign it a method that has a Person parameter
// because Employee derives from Person.
Action<Employee> addEmployeeToContacts = AddToContacts;
// You can also assign a delegate
// that accepts a less derived parameter to a delegate
// that accepts a more derived parameter.
addEmployeeToContacts = addPersonToContacts;
}
}
Kontravariance a anonymní funkce
Při práci s anonymními funkcemi (výrazy lambda) můžete narazit na neintuitivní chování související s kontravariancem. Podívejte se na následující příklad:
public class Person
{
public virtual void ReadContact() { /*...*/ }
}
public class Employee : Person
{
public override void ReadContact() { /*...*/ }
}
class Program
{
private static void Main()
{
var personReadContact = (Person p) => p.ReadContact();
// This works - contravariance allows assignment.
Action<Employee> employeeReadContact = personReadContact;
// This causes a compile error: CS1661.
// Action<Employee> employeeReadContact2 = (Person p) => p.ReadContact();
}
}
Zdá se, že toto chování je v rozporu: pokud kontravariance umožňuje přiřadit delegáta, který přijímá základní typ (Person) proměnné delegáta, který očekává odvozený typ (Employee), proč přímé přiřazení výrazu lambda selže?
Klíčovým rozdílem je odvození typu. V prvním případě je výraz lambda nejprve přiřazen k proměnné s typem var, což způsobí, že kompilátor odvodí typ lambda jako Action<Person>. Následné přiřazení bude Action<Employee> úspěšné kvůli pravidlům kontravariance pro delegáty.
V druhém případě kompilátor nemůže přímo odvodit, že výraz (Person p) => p.ReadContact() lambda by měl mít typAction<Person>, když je přiřazen .Action<Employee> Pravidla odvozování typů pro anonymní funkce automaticky nepoužívají kontravarianci při počátečním stanovení typu.
Alternativní řešení
Pokud chcete, aby přímé přiřazení fungovalo, můžete použít explicitní přetypování:
// Explicit cast to the desired delegate type.
Action<Employee> employeeReadContact = (Action<Person>)((Person p) => p.ReadContact());
// Or specify the lambda parameter type that matches the target delegate.
Action<Employee> employeeReadContact2 = (Employee e) => e.ReadContact();
Toto chování znázorňuje rozdíl mezi kontravariancem delegáta (který funguje po vytvoření typů) a odvození typu výrazu lambda (ke kterému dochází během kompilace).