Делегат может вызывать только такие методы, у которых тип возвращаемого значения и список параметров совпадают с соответствующими элементами объявления делегата.
Пример. Получить таблицу значений функции в заданном интервале
y= x+ x при [ 0, 5], y= x * x при [-5, 0]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
delegate double d(double x);
class Test
{
static double SUM(double x)
{
return x + x;
}
static double mult(double x)
{
return x * x;
}
static void myf(d F, double a)
{
Console.WriteLine(a.ToString("f1") + " " + F(a).ToString("f1"));
}
static void Main()
{
d F1 = new d(SUM);
for (int a = -5; a < 6; a++)
{
if (a > 0)
myf(F1, a);
else
myf(mult, a);
}
}
}
}
События События позволяют классу или объекту уведомлять другие классы или объекты о возникновении каких-либо ситуаций. Класс, отправляющий (или вызывающий) событие, называется издателем, а классы, принимающие (или обрабатывающие) событие, называются подписчиками.
в основе событий лежит делегат EventHandler и базовый класс EventArgs
public delegate void EventHandler( Object sender, EventArgs e)
public class EventArgs
Описание метода обработки события идентично описанию делегата, являющегося обработчиком событий. Описание обработчика событий должно соответствовать следующим соглашениям:
1. Определите метод с той же подписью, что и у делегата события.
· Тип возвращаемого значения — Void.
· Первый параметр называется sender и имеет тип Object. Это объект, вызвавший событие.
· Второй параметр называется e и имеет тип EventArgs или производный класс от EventArgs.
Это данные, специфичные для события.
· Этот метод принимает ровно два параметра.
2 Создайте экземпляр делегата с помощью ссылки на метод обработчика событий. Когда
вызывается экземпляр делегата, то он, в свою очередь, вызывает метод обработчика событий.
3 Добавьте экземпляр делегата к событию.
Пример
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class List
{
const int defaultCapacity = 4;
string[] items;
int count;
public List(int capacity = defaultCapacity)
{
items = new string[capacity];
}
public int Count
{
get { return count; }
}
public void Add(string item)
{
items[count] = item;
count++;
OnChanged();
}
protected virtual void OnChanged()
{
if (Changed != null) Changed(this, EventArgs.Empty);
}
// The event member that is of type EventHandler.
public event EventHandler Changed;
}
class Test
{
static int changeCount;
// Определите метод с той же подписью, что и у делегата события
static void ListChanged(object sender, EventArgs e)
{
changeCount++;
}
static void Main()
{
List names = new List();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(changeCount); // Outputs "4"
}
}
}
Для обработки каждого события используйте защищенный виртуальный метод. Это применимо только к нестатическим событиям на незакрытых классах и не может использоваться со структурами, закрытыми классами или статическими событиями.
Выполнение этих рекомендаций позволяет производным классам обрабатывать событие базового класса путем перегрузки защищенного метода. Имя защищенного метода virtual должно быть таким же, как и имя события, и содержать префикс On. Например, защищенный виртуальный метод для события с именемTimeChanged имеет имя OnTimeChanged.
В классе List объявляется член события Changed, указывающий на добавление нового элемента в список. Событие Changed вызывается виртуальным методом OnChanged, в котором сначала проверяется, имеет ли событие значение null (т. е. для события отсутствуют обработчики). Понятие вызова события в точности соответствует вызову делегата, представленного этим событием. Поэтому в языке не предусмотрены специальные конструкции для вызова событий.
Реакция клиента на событие реализуется с помощью обработчиков событий. Для вложения обработчиков событий используется оператор «+=», для удаления — оператор «-=».
Концепция анонимных методов, позволяет писать блоки неименованных встраиваемых выражений, которые можно выполнять в вызове делегата.
Создание анонимных методов является, по существу, способом передачи блока кода в качестве параметра делегата
В примере, приведенным выше вместо кода
class Test
{
static int changeCount;
// Определите метод с той же подписью, что и у делегата события
static void ListChanged(object sender, EventArgs e)
{
changeCount++;
}
static void Main()
{
List names = new List();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(changeCount); // Outputs "4"
}
}
Можно использовать код
class Test
{
static int changeCount;
static void Main()
{
List names = new List();
names.Changed += new EventHandler(delegate(object sender, EventArgs e){changeCount++; });
names.Add("Liz");
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(changeCount); // Outputs "3"
}
}
Или
class Test
{
static int changeCount;
static void Main()
{
List names = new List();
names.Changed += new EventHandler((object sender, EventArgs e) => { changeCount++; });
names.Add("Liz");
names.Add("Liz");
names.Add("Martha");
Console.WriteLine(changeCount); // Outputs "3"
}
}
Эволюция делегатов в C#(пример из MSDN)
В языке C# 1.0 разработчик создавал экземпляр делегата путем его явной инициализации с методом, определенным в каком-либо месте кода.
В C# версии 2.0 представлена концепция анонимных методов, которые позволяют писать блоки неименованных встраиваемых выражений, которые можно выполнять в вызове делегата.
В языке C# версии 3.0 представлены лямбда-выражения, сходные с анонимным методам, но более выразительные и лаконичные.
Две эти возможности вместе называются анонимными функциями.
class Test
{
delegate void TestDelegate(string s);
static void M(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
// Original delegate syntax required
// initialization with a named method.
TestDelegate testDelA = new TestDelegate(M);
TestDelegate testDelA1 = M;
// C# 2.0: A delegate can be initialized with
// inline code, called an "anonymous method." This
// method takes a string as an input parameter.
TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };
// C# 3.0. A delegate can be initialized with
// a lambda expression. The lambda also takes a string
// as an input parameter (x). The type of x is inferred by the compiler.
TestDelegate testDelC = (x) => { Console.WriteLine(x); };
// Invoke the delegates.
testDelA("Hello. My name is M and I write lines.");
testDelB("That's nothing. I'm anonymous and ");
testDelC("I'm a famous author.");
// Keep console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
Выражения анонимных функций
Анонимная функция — это выражение, представляющее собой подставляемое определение метода. Анонимная функция не имеет значения или тип сама по себе, но может быть преобразована в совместимый тип делегата или дерева выражения. Преобразование анонимной функции зависит от целевого типа преобразования. Если это тип делегата, то результатом преобразования является значение делегата, ссылающееся на метод, определяемый анонимной функцией. Если это тип дерева выражения, то результатом преобразования является дерево выражения, которое представляет структуру метода в виде структуры объекта.
Существует две синтаксические разновидности анонимных функций, а именно: лямбда_выражения и выражения_анонимных_методов. Практически для любых целей лямбда_выражения более конкретны и точны, чем выражения_анонимных_методов, которые остаются в языке в целях обратной совместимости.
лямбда_выражение:
подпись_анонимной_функции => тело_анонимной_функции
выражение_анонимного_метода:
delegate подпись_явной_анонимной_функциинеобязательно блок
Оператор => имеет такой же приоритет, как и присваивание (=) и обладает правой ассоциативностью.
Тип параметров анонимной функции в виде лямбда_выражения может задаваться явно или неявно. В списке явно типизированных параметров тип каждого параметра указывается явно. В списке неявно типизированных параметров типы параметров выводятся из контекста, в котором находится анонимная функция, в частности, когда анонимная функция преобразуется в совместимый тип делегата или дерева выражения, типы параметров предоставляются этим типом.
В анонимной функции с одним параметром с неявной типизацией в списке параметров можно опустить скобки. Другими словами запись анонимной функции вида
( param ) => выражение
можно сократить до:
param => выражение
Пример
namespace ConsoleApplication1
{
delegate int del(int i);
class Program
{
static void Main(string[] args)
{
del objdel = x => x + 1; // Implicitly typed, expression body
Console.WriteLine(objdel(5));
}
}
}
Список параметров анонимной функции в виде выражения_анонимного_метода является необязательным. Если они заданы, то параметры должны быть явно типизированы Если они не задаются, то анонимную функцию можно преобразовать в делегат с любым списком параметров, не содержащих параметров out.
Ниже приведены некоторые примеры анонимных функций.
x => x + 1 // Implicitly typed, expression body
x => { return x + 1; } // Implicitly typed, statement body
(int x) => x + 1 // Explicitly typed, expression body
(int x) => { return x + 1; } // Explicitly typed, statement body
(x, y) => x * y // Multiple parameters
() => Console.WriteLine() // No parameters
delegate (int x) { return x + 1; } // Anonymous method expression
delegate { return 1 + 1; } // Parameter list omitted
Поведение лямбда_выражений и выражений_анонимных_методов совпадает за исключением следующих моментов:
· выражения_анонимных_методов позволяют опускать список параметров целиком, обеспечивая возможность преобразования в типы делегатов с любым списком параметров значения.
· лямбда_выражения позволяют опускать и выводить типы параметров, тогда как в выражениях_анонимных_методов типы параметров должны быть указаны явно.
· Тело лямбда_выражения может быть выражением или блоком оператора, тогда как тело выражения_анонимного_метода должно быть блоком оператора.
· Поскольку тело_выражения могут иметь только лямбда_выражения, выражения_анонимных_методов нельзя преобразовать в тип дерева выражения.
namespace ConsoleApplication1
{
delegate int del(int i);
class Program
{
static void Main(string[] args)
{
del objdel = x => x + 1; // Implicitly typed, expression body
Console.WriteLine(objdel(5));
objdel = x => { return x + 1; }; // Implicitly typed, statement body
objdel = (int x) => x + 1; // Explicitly typed, expression body
objdel = (int x) => { return x + 1; }; // Explicitly typed, statement body
objdel = delegate(int x) { return x + 1; }; // Anonymous method expression
objdel = delegate { return 1 + 1; }; // Parameter list omitted
}
}
}
Лямбда-выражение
Лямбда-выражение — это анонимная функция, которая содержит выражения и операторы и может использоваться
Для создания делегатов
Для создания типов дерева выражений.
Пример
namespace ConsoleApplication1
{
delegate int del(int i);
class Program
{
static void Main(string[] args)
{
del objdel = x => x + 1;
Console.WriteLine(objdel(5));
Expression<del> myET = x => x * x;
del delobj = myET.Compile();
int i = delobj(5);
Console.WriteLine(i);
}
}
}
Или создание типа дерева выражений
namespace ConsoleApplication1
{
delegate int del(int i);
class Program
{
static void Main(string[] args)
{
Expression<del> myET = x => x * x;
int i = myET.Compile()(6);
Console.WriteLine(i);
}
}
}
Action - делегат
Пространство имен: System
Инкапсулирует метод, который не принимает ни одного параметра и не возвращает значений.
public delegate void Action()
Этот делегат можно использовать для передачи метода в качестве параметра без явного объявления пользовательского делегата.
Пример
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Expression<Action> printExpr = () => Console.WriteLine("hhh");
// Compiling and invoking the expression tree.
printExpr.Compile()();
}
}
}
Func<TResult> - делегат
Инкапсулирует метод без параметров и возвращает значение типа, указанного в параметре TResult.
Этот делегат можно использовать для представления метода, который можно передавать в качестве параметра без явного объявления пользовательского делегата. Пример
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static bool met() { return true; }
static void Main(string[] args)
{
Expression<Func<bool>> printExpr = () => met();
// Compiling and invoking the expression tree.
Console.WriteLine(printExpr.Compile()());
}
}
}
Создание типа дерева выражений:
Можно создать дерево выражений на основе анонимного лямбда-выражения с использованием компилятора C# (как в примере выше) или создать деревья выражений вручную с использованием пространства имен System.Linq.Expressions.
Абстрактный класс Expression обеспечивает корень иерархии класса, используемый для моделирования деревьев выражений.
Классы в этом пространстве имен, которые наследуются от Expression, например MemberExpression и ParameterExpression, используются для представления узлов в дереве выражения.
Пример создания выражения
BinaryExpression – класс представляет выражение, содержащее бинарный оператор.
В следующем примере создается объект BinaryExpression, представляющий вычитание одного числа из другого.
class Program
{
static void Main(string[] args)
{
System.Linq.Expressions.BinaryExpression binaryExpression =
System.Linq.Expressions.Expression.MakeBinary(
System.Linq.Expressions.ExpressionType.Subtract,
System.Linq.Expressions.Expression.Constant(53),
System.Linq.Expressions.Expression.Constant(14));
Console.WriteLine(binaryExpression.ToString());
}
}
Выполнение *дерева выражений
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConsoleApplication1
{
delegate int del();
class Program
{
static void Main(string[] args)
{
BinaryExpression binaryExpression = Expression.MakeBinary(ExpressionType.Subtract, Expression.Constant(13), Expression.Constant(4));
Expression<del> myET = Expression.Lambda<del>(binaryExpression);
del delobj = myET.Compile();
int i = delobj();
Console.WriteLine(i);
}
}
}
Лямбда операторов напоминает выражение-лямбду, за исключением того, что оператор (или операторы) заключается в фигурные скобки:
Пример Код не содержит ошибок
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Action printTwoLines = () =>
{
Console.WriteLine("Print arg:");
Console.WriteLine("nnnn");
};
printTwoLines();
}
}
}
Этот код содержит ошибку
Expression<Action> printTwoLines = () => { Console.WriteLine("Print arg:"); };
Ошибка 1 Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражения
Компилятор C# может создавать деревья выражений только из лямбд выражений (или однострочных лямбд). Эот компилятор не может выполнить синтаксический анализ лямбд операторов (или многострочных лямбд).
BlockExpression - класс
Представляет блок, содержащий последовательность выражений, в котором могут быть определены переменные.
Пример (MSDN)
namespace ConsoleApplication1
{
class Program
{
static int CSharpFact(int value)
{
int result = 1;
while (value > 1)
{
result *= value--;
}
return result;
}
static void Main(string[] args)
{
Console.WriteLine(CSharpFact(5));
}
}
class Program
{
static int CSharpFact(int value)
{
int result = 1;
while (value > 1)
{
result *= value--;
}
return result;
}
static void Main(string[] args)
{
Expression<Func<int,int>> printExpr = (v) => CSharpFact(v);
Console.WriteLine(printExpr.Compile()(5));
Console.WriteLine(CSharpFact(5));
}
}
В следующем примере показано создание дерева выражений, позволяющего рассчитать факториал числа.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ParameterExpression value = Expression.Parameter(typeof(int), "value");
// Creating an expression to hold a local variable.
ParameterExpression result = Expression.Parameter(typeof(int), "result");
// Creating a label to jump to from a loop.
LabelTarget label = Expression.Label(typeof(int));
// Creating a method body.
BlockExpression block = Expression.Block(
// Adding a local variable.
new[] { result },
// Assigning a constant to a local variable: result = 1
Expression.Assign(result, Expression.Constant(1)),
// Adding a loop.
Expression.Loop(
// Adding a conditional block into the loop.
Expression.IfThenElse(
// Condition: value > 1
Expression.GreaterThan(value, Expression.Constant(1)),
// If true: result *= value --
Expression.MultiplyAssign(result,
Expression.PostDecrementAssign(value)),
// If false, exit the loop and go to the label.
Expression.Break(label, result)
),
// Label to jump to.
label
)
);
// Compile and execute an expression tree.
int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);
Console.WriteLine(factorial);
}
}
}
LINQ (Language-Integrated Query)
Все операции запроса LINQ состоят из трех различных действий.
1. Получение источника данных.
2. Создание запроса.
3. Выполнение запроса.
Пример (MSDN)
В примере в качестве источника данных для удобства используется массив целых чисел;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
}
}
}
Выражения запросов составляются в соответствии с декларативным синтаксисом запроса
Основные операции запросов LINQ (C#)
В запросе LINQ первым идет предложение from для указания источника данных
Наиболее распространенной операцией запроса является применение фильтра в виде логического выражения. Фильтр приводит к возвращению запросом только тех элементов, для которых выражение является истинным. Результат создается с помощью предложения where.
Предложение orderby сортирует элементы возвращаемой последовательности в зависимости от компаратора по умолчанию для сортируемого типа. Например,
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = new int[7] { 0,4, 22, 13, 34, 5, 6 };
// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
orderby num ascending
select num;
// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
}
}
}
orderby num descending
Предложение group позволяет группировать результаты на основе указанного ключа.
В выражении запроса предложение select задает тип значений, получаемых при выполнении запроса.
Подробнее в http://msdn.microsoft.com/ru-ru/library/bb310804(v=vs.100).aspx
Использование лямбда-выражений в запросах
Во время компиляции выражения запроса преобразуются в то, что понятно CLR — вызовы методов. Эти методы называются стандартными операторами запросов и имеют такие имена, как Where, Select, GroupBy
В следующем примере показано простое выражение запроса и семантически эквивалентный ему запрос, написанный как запрос на основе метода.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int[] numbers = { 5, 10, 8, 3, 6, 12 };
//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
foreach (int i in numQuery1)
{
Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
Console.Write(i + " ");
}
// Keep the console open in debug mode.
Console.WriteLine(System.Environment.NewLine);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
}
Условное выражение (num % 2 == 0) передается в качестве встроенного аргумента методу Where: Where(num => num % 2 == 0) и является лямбда-выражением.
int[] numbers = { 3, 4, 5};
Console.WriteLine( numbers.Average());
Пример
В следующем примере показано простое выражение запроса и семантически эквивалентный ему запрос, написанный как запрос на основе метода.
namespace ConsoleApplication1
{
public class Student
{
public string First { get; set; }
public string Last { get; set; }
public int ID { get; set; }
public int[] Scores;
public override string ToString()
{
return First + "\t " + Last + "\t" + ID;
}
}
public class Program
{
static void Main(string[] args)
{
Student[] students = new Student[4]{new Student {First="Sveta", Last="Omelchenko", ID=111, Scores= new int[] {97, 92, 81, 60}},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new int[] {5, 4, 3, 4}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= new int[] {88, 94, 65, 91}},
new Student {First="Cesar", Last="Garcia ", ID=114, Scores= new int[] {97, 89, 85, 82}}};
foreach (Student s in students)
{
Console.WriteLine(s.ToString());
}
//// Produce a filtered sequence of unmodified Students.
//IEnumerable<Student> studentQuery1 =
// from student in students
// where student.ID > 111
// select student;
IEnumerable<Student> numQuery1 = students.Where(stud => stud.ID > 111);
Console.WriteLine();
Console.WriteLine("Query1: select range_variable");
foreach (Student s in numQuery1)
{
Console.WriteLine(s.ToString());
}
// Produce a filtered sequence of elements that contain
// only one property of each Student.
//IEnumerable<String> studentQuery2 =
// from student in students
// where student.ID > 111
// select student.Last;
IEnumerable<String> numQuery2 = students.Where(stud => stud.ID > 111).Select(stud => stud.Last);
Console.WriteLine();
Console.WriteLine("studentQuery2: select range_variable.Property");
foreach (string s in numQuery2)
{
Console.WriteLine(s);
}
// Produce a filtered sequence of ints from
// the internal array inside each Student.
//IEnumerable<int> studentQuery4 =
// from student in students
// where student.ID > 111
// select student.Scores[0];
IEnumerable<int> numQuery4 = students.Where(stud => stud.ID > 111).Select(stud => stud.Scores[0]);
Console.WriteLine("\r\n studentQuery4: select range_variable[index]");
foreach (int i in numQuery4)
{
Console.WriteLine("First score = {0}", i);
}
// Produce a filtered sequence of doubles
// that are the result of an expression.
//IEnumerable<double> studentQuery5 =
// from student in students
// where student.ID > 111
// select student.Scores[0] * 1.1;
IEnumerable<double> numQuery5 = students.Where(stud => stud.ID > 111).Select(stud => stud.Scores[0]*1.1);
Console.WriteLine("\r\n studentQuery5: select expression");
foreach (double d in numQuery5)
{
Console.WriteLine("Adjusted first score = {0}", d);
}
//// Produce a filtered sequence of doubles that are
//// the result of a method call.
//IEnumerable<double> studentQuery6 =
// from student in students
// where student.ID > 111
// select student.Scores.Average();
IEnumerable<double> numQuery6 = students.Where(stud => stud.ID > 111).Select(stud => stud.Scores.Average());
Console.WriteLine("\r\n studentQuery6: select expression2");
foreach (double d in numQuery6)
{
Console.WriteLine("Average = {0}", d);
}
// Produce a filtered sequence of anonymous types
// that contain only two properties from each Student.
//var studentQuery7 =
// from student in students
// where student.ID > 111
// select new { student.First, student.Last };
IEnumerable<string> numQuery7 = students.Where(stud => stud.ID > 111).Select(stud => stud.First + stud.Last);
Console.WriteLine("\r\n studentQuery7: select new anonymous type");
foreach (string item in numQuery7)
{
Console.WriteLine("{0}", item);
}
}
}
}
Универсальные шаблоны
Пример
class MyClass
{
int a;
int b;
public MyClass(int a, int b)
{
this.a = a;
this.b = b;
}
public override string ToString()
{
return String.Format("{0} {1}", a, b);
}
}
class Program
{
static void Main(string[] args)
{
MyClass myobject = new MyClass(3, 6);
Console.WriteLine(myobject);
}
}
using System;
class MyClass<T>
{
T a;
T b;
public MyClass(T a, T b)
{
this.a = a;
this.b = b;
}
public override string ToString()
{
return String.Format("{0} {1}", a, b);
}
}
class Program
{
static void Main(string[] args)
{
MyClass<string> myobject = new MyClass<string>("3", "6");
Console.WriteLine(myobject);
}
}
using System;
class MyClass<T1,T2>
{
T1 a;
T2 b;
public MyClass(T1 a, T2 b)
{
this.a = a;
this.b = b;
}
public override string ToString()
{
return String.Format("{0} {1}", a, b);
}
}
class Program
{
static void Main(string[] args)
{
MyClass<string,int> myobject = new MyClass<string,int>("3", 6);
Console.WriteLine(myobject);
}
}
Использование неуниверсальных классов коллекции (msdn и спецификация C#)
Пример
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
System.Collections.ArrayList list1 = new System.Collections.ArrayList();
list1.Add(3);
list1.Add(105);
System.Collections.ArrayList list2 = new System.Collections.ArrayList();
list2.Add("It is raining in Redmond.");
list2.Add("It is snowing in the mountains.");
}
}
}
Любые ссылки или типы значения, добавляемые к классу коллекции ArrayList, неявно приводятся к Object. Если в качестве элементов выступают типы значений, то для них необходимо выполнить упаковку-преобразование при добавлении к списку и распаковку-преобразование при извлечении из списка. Операции приведения, упаковки-преобразования и распаковки-преобразования снижают производительность. Влияние операций упаковки-преобразования и распаковки-преобразования в сценариях обработки больших коллекций может быть весьма значительным.
Еще одно ограничение состоит в отсутствии проверки типа во время компиляции. Поскольку класс коллекции ArrayList приводит все к типу Object, невозможно предотвратить выполнение клиентским кодом во время компиляции действий, подобных следующим:
System.Collections.ArrayList list = new System.Collections.ArrayList();
// Add an integer to the list.
list.Add(3);
// Add a string to the list. This will compile, but may cause an error later.
list.Add("It is raining in Redmond.");
int t = 0;
// This causes an InvalidCastException to be returned. Ошибка !!
foreach (int x in list)
{
t += x;
}
Несмотря на то, что комбинирование строк и int в единый класс коллекции ArrayList вполне допустимо, а иногда и специально выполняется (при создании разнородной коллекции), во многих случаях это может привести к ошибке программы, которую можно будет обнаружить только при выполнении.
В универсальном шаблоне коллекции List<T> в пространстве имен N:System.Collections.Generic та же операция добавления элементов в коллекцию выглядит следующим образом:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// The .NET Framework 2.0 way to create a list
List<int> list1 = new List<int>();
// No boxing, no casting:
list1.Add(3);
// Compile-time error:
// list1.Add("It is raining in Redmond.");
}
}
}
Единственным различием в клиентском коде между List<T> и ArrayList является аргумент типа <T> в разделе объявления и создания экземпляра. Благодаря этому небольшому усложнению кода можно создать список, который будет не только безопаснее, чем ArrayList, но и существенно быстрее, особенно если элементами списка являются типы значений.
Дата: 2019-03-05, просмотров: 255.