Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Методические материалы

.pdf
Скачиваний:
9
Добавлен:
07.02.2016
Размер:
606.53 Кб
Скачать

Новые возможности Visual Studio 2008

11

return s1.Length - s2.Length;

});

Метод CompareLength нам больше не нужен – мы описали алгоритм сравнения прямо внутри вызова метода Sort. Но стал ли код от этого понятнее? С одной стороны мы больше не отрываем код, сравнивающий строки от того места, где выполняется сравнение, и это конечно хорошо. Но с другой стороны получившаяся конструкция чрезвычайно неуклюжа: фактически мы поместили описание метода сортировки внутрь списка параметров. Фигурные скобки, обозначающие блок кода, выглядят довольно дико внутри круглых скобок, обозначающих список параметров метода.

Возможно, вам интересно, какой код генерируется при компиляции анонимных методов. Ну а может быть, вы догадались сами. Название «анонимный метод» очень красноречиво. Компилятор выносит весь код, размещенный внутри фигурных скобок, в отдельный метод, создает делегат, ссылающийся на этот метод, и передает этот делегат методу Sort. После компиляции отличить последний вариант сортировки от первого можно будет только по бессмысленному названию метода выполняющего сортировку.

Все это уже было в С# 2.0 Какие же сюрпризы преподнесет нам C# 3.5? Теперь ту же самую задачу можно описать куда лаконичнее. Вот так:

List<string> city = new List<string>() {"Москва", "Санкт-Петербург", "Екатеринбург", "Новосибирск"};

city.Sort((s1, s2) => s1.Length - s2.Length);

foreach (string s in city)

{

Console.WriteLine(s);

}

Обратите внимание на странную конструкцию, находящуюся внутри скобок в методе Sort. Это – лямбда-выражение. Именно оно описывает алгоритм сортировки строк. Оператор => называется лямбда-оператором.

Лямбда-выражение должно выглядеть следующим образом: слева от стрелки должен стоять один или несколько входных параметров, разделенных запятой. Если параметров несколько, их необходимо взять в скобки. Справа от стрелки должно быть выражение, зависящее от входных параметров.

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

(список параметров) => выражение

Тогда соответствующий анонимный метод будет выглядеть следующим образом:

delegate (список параметров)

{

return выражение;

}

Точно так же как и анонимный метод, лямбда-выражение можно поместить в делегат:

//Объявляем переменную-делегат

Comparison<int> c;

12 Новые возможности Visual Studio 2008

//Заносим в нее значение

c = (i, j) => Math.Abs(i)-Math.Abs(j);

Это лямбда-выражение выполняет сравнение по модулю.

Лямбда-выражение может вообще не иметь параметров. В таком случае перед стрелкой необходимо написать пустые скобки:

() => выражение;

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

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

city.Sort((int s1, int s2) => s1.Length - s2.Length);

Внутри выражения, стоящего справа от стрелки можно использовать не только входные параметры. Помимо того, можно использовать статические члены различных классов, в частности мы уже использовали метод Abs класса Math. Использование экземплярных членов того класса, в котором задается лямбда-выражение, недопустимо. Однако, как это ни странно, разрешается использование локальных переменных. Например, такой несколько неочевидный код отлично компилируется:

int sign = -1;

city.Sort((s1, s2) => (s1.Length - s2.Length)*sign);

Обратите внимание, что в лямбда-выражение входит локальная переменная sign, хотя в списке параметров она отсутствует.

Напоследок хочется отметить, что лямбда-выражения наиболее близки к тому, что математики понимают под функцией. С их точки зрения функция – это некоторый объект, который принимает чтото на входе и выдает что-то на выходе, не производя никаких побочных эффектов. И математики обычно стремятся представить функцию в виде одного единственного выражения. Более того, даже обычный «математический» способ записи функции напоминает лямбда-выражения. Вот, например, так выглядит функция, возводящая число в квадрат:

f : x x2

Неправда ли сильно напоминает лямбда-выражение?

LINQ

Аббревиатура LINQ расшифровывается как Language Integrated Querу (Я затрудняюсь корректно перевести это на русский язык). LINQ – самое главное, самое многообещающее и мощное нововведение в языке C# 3.0 (Скажу по секрету: именно ради LINQ я установил себе Visual Studio 2008). Можно даже

Новые возможности Visual Studio 2008

13

сказать, что все остальное, о чем мы говорили до этого, введено в язык для того, чтобы сделать возможным появление LINQ.

Что же это такое? Если вкратце, то это специальное расширение C# (и VB) позволяющее использовать в программе запросы, очень похожие на запросы SQL. А запросы эти в свою очередь позволяют просто и элегантно решать многие сложные задачи.

LINQ - очень сложная тема. Я не ставлю себе целью полностью и подробно описать его. Я постараюсь лишь дать общее представление о возможностях LINQ с тем, чтобы читатель продолжил изучение самостоятельно.

Давайте начнем с небольшого примера, на котором я попробую объяснить, что такое LINQ. Предположим, что у нас есть список названий городов:

List<string> city = new List<string>()

{"Москва", "Санкт-Петербург", "Екатеринбург", "Новосибирск", "Хабаровск","Нижний Новгород","Владимир","Клин","Оленегорск"};

Я хочу получить новый список, в котором эти города будут отсортированы по алфавиту и приведены в нижний регистр. Кроме того, я хочу, чтобы в этот список попали только те города, в названии которых нет пробела. Это совсем не сложная задача, давайте решим ее обычными средствами:

List<string> result = new List();

foreach(string s in city)

{

if (!s.Contains(" "))

{

result.Add(s.ToLower);

}

}

result.Sort();

Действительно, получился простой и тривиальный фрагмент кода. Тем не менее, он достаточно громоздок: мы создаем новый список, используем цикл, ветвление.… Ради совсем, в общем-то, простой задачи. Давайте посмотрим, как ее можно решить с помощью LINQ:

IEnumerable<string> result =

from c in city orderby c where !c.Contains(" ") select c.ToLower();

Согласитесь, такое решение значительно короче. Правда, оно непонятно – ведь мы еще не знаем что такое LINQ. Давайте попробуем разобраться, что здесь написано не прибегая к помощи справочника по LINQ. Во-первых, каждый разработчик, имевший когда-либо дело с базами данных, увидит, что это выражение очень похоже на SQL. Ключевые слова расположены не в том порядке, но в целом очень похоже. В таком случае после ключевого слова from должен идти источник данных (в SQL это таблица), и логично предположить, что в нашем случае в качестве источника сгодится список. Orderby повидимому задает порядок сортировки – сортировать мы будем по значению строки. Строки можно отфильтровать с помощью ключевого слова where и действительно после него идет булево выражение. Ну а select определяет, что мы хотим получить на выходе – не удивительно, что после него используется метод ToLower(), ведь мы хотели привести названия к нижнему регистру. Все сходится. Простейшие выражения LINQ можно писать почти интуитивно.

14 Новые возможности Visual Studio 2008

Запросы LINQ совершенно необязательно писать в таком непривычном виде. Существует альтернативная, более традиционная запись:

IEnumerable<string> result =

city.Where(c => !c.Contains(" ")).OrderBy(c => c).Select(c => c.ToLower());

Это тот же самый запрос, что и раньше. Но на этот раз я использовал три метода: Where, OrderBy и Select. Каждый из них принимает в качестве параметра делегат (я использовал лямбда-выражения) и возвращает IEnumerable<T> с которым работает следующий метод в цепочке. Интересно, а откуда взялись эти методы? Неужели в .Net Framework 3.5 тип List<T> и интерфейс IEnumerable<T> были переработаны и в них были добавлены все эти методы? Разумеется это не так. Все эти методы присоединенные, то есть они объявлены в совершенно другом классе и только выглядят как методы List и IEnumerable. Точнее все они присоединяются именно к интерфейсу IEnumerable, просто List реализует этот интерфейс. Давайте посмотрим, что делает каждый из методов.

Where – фильтрует элементы списка и пропускает сквозь себя только те, которые удовлетворяют условию. Условие нужно задать в виде делегата, принимающего на вход элемент списка и возвращающий булево значение. В нашем примере используется лямбда-выражение.

OrderBy – сортирует список. Критерий сортировки нужно задать в виде делегата, принимающего на вход элемент списка и возвращающий тот элемент, по которому надо отсортировать. Поскольку мы хотим отсортировать строки по их значениям, мы использовали лямбда функцию, возвращающую то, что было предано на вход.

Select – этот метод формирует новый список. Ему тоже нужно передать делегат, принимающий на вход элемент списка и выдающий элемент нового списка.

Теперь я могу более подробно объяснить, что такое LINQ. Он состоит из двух частей:

Множество присоединенных методов, наподобие тех, что мы только что рассмотрели, каждый из которых позволяет выполнить какую-нибудь операцию со списком объектов.

Синтаксический анализатор, который транслирует SQL-подобные выражения в вызовы этих методов. Это важный момент! Вне зависимости от того, какой синтаксис мы выбираем, после компиляции мы получим одно и то же.

Разумеется возможности LINQ не ограничиваются запросами к спискам элементарных типов. Столь же легко можно обрабатывать коллекции произвольных классов или структур. В качестве примера создадим список процессов, занимающих более 10 Мб в оперативной памяти. Получить список всех процессов можно с помощью статического метода GetProcesses находящегося в классе Process:

IEnumerable<Process> result =

from p in Process.GetProcesses() where p.WorkingSet64 > 10000000 select p;

Этот запрос возвращает значительно больше информации чем нам нужно: он возвращает список экземпляров класса Process, в то время как нас интересуют только названия процессов. Запросы LINQ позволяют легко изменять тип возвращаемых значений:

IEnumerable<string> result =

from p in Process.GetProcesses() where p.WorkingSet64 > 10000000 select p.ProcessName;

Но что делать, если я хочу получить на выходе не только название процесса, но и объем оперативной памяти, который он занимает? Можно конечно вернуть экземпляр Process целиком – там есть нужное

Новые возможности Visual Studio 2008

15

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

var result =

from p in Process.GetProcesses() where p.WorkingSet64 > 10000000 select new { Name = p.ProcessName, WorkingSet = p.WorkingSet64};

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

Я думаю, что приведенных выше примеров достаточно для того, чтобы уловить основную идею LINQ и поверить в то, что это очень мощный и удобный инструмент. Теперь давайте рассмотрим несколько расширений LINQ предназначенных для работы со специфическими объектами.

LINQ to DataSet

В LINQ входит ряд компонентов, предназначенных для работы с конкретными наборами классов. Как несложно догадаться из названия, LINQ to DataSet предназначен для создания LINQ-запросов к отсоединенным наборам данных.

LINQ to SQL

Сам по себе LINQ предназначен для работы с данными находящимися в памяти процесса и ничего не знает ни о базах данных, ни о том, как с ними работать. LINQ to SQL это достаточно сильно обособленная часть LINQ как раз и отвечающая за связь с базой данных, а именно, за:

Подключение к базе данных

Преобразование запроса LINQ в запрос SQL

Генерация объектов, представляющих строки и таблицы в базе данных.

LINQ to XML

LINQ включает в себя набор специальных методов, упрощающих написание запросов к иерархическим данным, представленным в виде XML. Кроме того, LINQ to XML значительно упрощает процесс генерации XML-разметки.

WPF и WCF – новый подход к старым задачам

В состав .Net Framework 3.0 входят две новых библиотеки: Windows Presentation Foundation и Windows Communication Foundation. И та и другая предназначены для замены существующих библиотек: предполагается что WPF заменит собой Windows Forms, а WCF – web-службы ASP.Net и отчасти

Remoting.

Windows Presentation Foundation

Итак, Windows Presentation Foundation – это новая библиотека, предназначенная для разработки полнофункциональных графических приложений, то есть представляющая возможности, аналогичные Windows Forms. Если нам предлагают замену, то следовательно Windows Forms чем-то плоха. Давайте попробуем понять, что в ней нет так.

Вот две основных проблемы:

Тесная связь со старым GUI

Низкая производительность при рисовании графики

16 Новые возможности Visual Studio 2008

Большинство элементов управления Windows Forms представляют собой довольно-таки тонкую оболочку вокруг соответствующих элементов управления Windows. Нельзя сказать, чтобы само по себе это было плохо, даже наоборот – это позволяет достичь высокой скорости отрисовки этих элементов управления и обеспечивает единообразность внешнего вида различных приложений. Проблема в том, что эти элементы управления позволяют решить лишь ограниченный круг задач. Например, выпадающий список (ComboBox) не может показывать картинки - только текст. Календарь (MonthCalendar) позволяет выделять даты только полужирным начертанием и нет никакого способа выделить дату цветом или рамкой. Изменить цвет кнопки можно, но при этом изменяется цвет только у центральной части, а выпуклая рамка останется прежнего цвета. Все это мелкие проблемы, но их очень много и вместе они образуют одну большую – стандартные элементы управления недостаточно гибки. Хорошо, но ведь мы можем разрабатывать собственные элементы управления! Да, но тут вылезает вторая проблема: производительность системы GDI+, отвечающей за рисование графики в Windows Forms, очень мала. Самодельные элементы управления отрисовываются слишком медленно. Приходится исхитряться, оптимизировать рисование элемента управления, но результат все равно получается не очень хорош…

Объединившись вместе эти две проблемы «убили» Windows Forms. Теперь нам предлагается разрабатывать оконные приложения с помощью WPF. У этой библиотеки существует всего один большой недостаток: она непохожа на Windows Forms. Совсем. И это конечно затрудняет переход на нее.

Каким же образом WPF избежала проблем присущих Windows Forms? Прежде всего, WPF не использует GDI+ и не использует вообще GDI. Для рисования графики она использует DirectX. На первый взгляд такой подход кажется странным: ведь DirectX в первую очередь предназначен для отображения 3D-графики. Неужели нам предлагают рисовать трехмерные интерфейсы? К счастью нет. DirectX отлично справляется и с рисованием 2D изображений, и делает это значительно быстрее чем GDI. Интерфейс остался двухмерным, но теперь он отрисовывается значительно быстрее, чем раньше.

Вторая проблема присущая Windows Forms, недостаточная гибкость, была решена следующим путем: отдельные компоненты WPF значительно «мельче» чем компоненты Windows Forms. Если сравнить интерфейс со зданием, то можно сказать, что приложения Windows Froms стоятся из готовых панелей, а приложения WPF – из кирпичей.

XAML

Если мы заглянем в проект, написанный с использованием WPF, то обнаружим в нем множество файлов с расширением xaml. Внимательно присмотревшись можно обнаружить, что именно они определяют интерфейс приложения – для каждого окна приложения создается отдельный файл. XAML расшифровывается как eXtended Application Markup Landuage и как несложно догададаться представляет собой XML-документ (Обратите внимание, что аббревиатура XAML читается несколько неожиданно: zammel).

<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WPF Window" Height="300" Width="300">

<Grid Width="274">

<Button Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="button1" VerticalAlignment="Top" Width="75">Button</Button>

</Grid>

</Window>

Новые возможности Visual Studio 2008

17

Давайте попробуем понять, почему разработчики WPF решили использовать XML для описания интерфейса. А как вообще можно описывать интерфейс? В Windows Forms для этого использовался метод InitializeComponent, который создавался редактором формы. Внутри этого метода находился обычный код, то есть визуальный редактор умел автоматически создавать код C# (или VB), описывающий интерфейс и умел читать его обратно без компиляции проекта и отображать интерфейс. Надо сказать, что со второй задачей он справлялся не очень хорошо – зачастую проект отлично компилировался и работал, но визуальный редактор не мог прочитать им же самим написанный код и отказывался редактировать форму. Если же программист пытался самостоятельно изменить что-либо в InitializeComponent, проблемы возникали очень часто. Существует другой способ описывать интерфейс. В Delphi для этого использовались специальные файлы, в которых интерфейс описывался в формате, удобном для редактора, но, увы, недокументированном и непонятном для программиста. Редактор форм всегда корректно работал, но программист был полностью лишен возможности изменить что-либо в этом файле, а некоторых вещей редактор не умел, например, изменять класс, от которого наследовалась форма.

XAML объединяет лучшее обоих подходов. С одной стороны XML удобен для визуального редактора, и он всегда способен отобразить корректный файл XAML. С другой, язык XAML тщательно документирован и разработчик может вручную редактировать интерфейс, если его не устраивают возможности визуального редактора. Более того, существуют сторонние приложения, позволяющие редактировать или создавать XAML. Например существует плагин для Adobe Illustrator, который позволяет сохранить векторное изображение в формате XAML.

Визуальные элементы интерфейса

WPF включает в себя большой набор «стандартных» элементов управления встречающихся практически во всех приложениях: кнопка, надпись, поле ввода, список, выпадающий список, «галочка», полоса прокрутки, индикатор степени завершения, набор вкладок и т.д. Фактически они повторяют элементы управления Windows Forms. Я не буду подробно останавливаться на этих элементах управления, поскольку они похожи на любой другой набор стандартных элементов управления.

Однако некоторых, казалось бы, совершенно необходимых элементов управления вы не найдете. Например, бесполезно искать кнопку с картинкой. Тем не менее, кнопку с картинкой сделать можно: для этого нужно просто поместить изображение внутрь кнопки. Дело в том, что в кнопку можно поместить абсолютно любой элемент управления, в том числе можно поместить одну кнопку внутрь другой:

Это принцип применим практически ко всем элементам управления WPF. Внутрь выпадающего списка можно с легкостью поместить «галочки», а во всплывающую подсказку – изображения.

Внешний вид элементов управления

В отличие от Windows Forms, WPF позволяет очень гибко настраивать внешний вид элементов управления. Во-первых, набор свойств, определяющих внешний вид элемента шире чем в Windows Forms. Помимо стандартных цвета текста и цвета фона, мы можем задавать внешнее и внутренне поле большинства элементов управления, можем повернуть или сжать любой элемент управления, можем даже применить к нему специальный эффект, например, размыть.

18 Новые возможности Visual Studio 2008

Однако этим возможность настройки не ограничивается. Дело в том, что WPF позволяет полностью изменить внешний вид практически любого элемента управления. Описание элемента управления как бы состоит из двух частей: функциональной и визуальной. Например, кнопка, это нечто нажимающееся, то есть имеющее событие OnClick, и изменяющее внешний вид при нажатии и наведении мыши. У кнопки есть стандартный шаблон, определяющий ее внешний вид по-умолчанию, но этот шаблон можно полностью заменить на другой. Можно сделать кнопку круглой или восьмиугольной, плоской или вогнутой. Всех этих изменений можно добиться не написав ни строки кода на C# или VB. Шаблон задается с помощью XAML.

Еще одно очень важное отличие WPF от Windows Forms состоит в том, как описывается внешний вид элемента управления. В Windows Forms элемент управления рисуется с помощью различных методов класса Canvas, и программист вынужден писать код, рисующий картинку. В WPF элемент управления составляется из более мелких элементов. Дело в том, что в состав WPF помимо обычных элементов интерфейса, таких как кнопки и надписи, входят различные графические примитивы: прямоугольник, овал, прямая линия, рамка, кривая Безъе. И если мы хотим изменить внешний вид кнопки мы должны составить ее из этих примитивов.

Контейнеры

Давайте вспомним, каким образом можно было задавать размер элементов управления в Windows Forms. Если абстрагироваться от названий конкретных свойств, то мы получим следующее: элемент управления можно было либо привязать к любой из границ контейнера, в котором он находился, и зафиксировать его размер, либо можно было задать расстояние до границ, и при этом элемент управления начинал растягиваться вместе с контейнером. На практике это приводило к тому, что разметы всех элементов управления были фиксированы, за исключением одного центрального элемента, который «тянулся», занимая оставшееся место. Часто такое поведение не устраивало разработчика: например довольно трудно бы сделать так, чтобы два элемента управления, например два списка, занимали половину окна каждый. Обычно форма создавалась в расчете на определенное разрешение экрана, а при других выглядела странно – либо слишком пустой, либо слишком тесно набитой, а то и вообще не влезала на экран.

В Windows Presentation Foundation все иначе. Существует целый ряд контейнеров, которые управляют положением и размерами размещенных в них элементов управления. Давайте рассмотрим их немного подробнее

Grid (сетка)

Сетка во многом похожа на таблицу в HTML. В сетке необходимо создать несколько строк и столбцов. Для каждой строки и столбца нужно указать размеры, причем это можно сделать как в абсолютных величинах, так и в долях от общего размера сетки. В каждой из получающихся ячеек можно размещать один или несколько элементов управления, которые будут привязываться к границам ячейки, и ничего не будут знать о размерах сетки в целом. Можно сделать так, чтобы один элемент управления занимал несколько ячеек. В качестве примера использования сетки я приведу окно, содержащее два списка и кнопки, перемещающие элементы между списками:

Новые возможности Visual Studio 2008

19

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

UniformGrid (однородная сетка)

Однородная сетка жестко контролирует размер элементов размещенных в ней – она выдает всем им одинаковое количество места. Элемент управления может быть меньше чем ячейка, однако обычно он занимает все отведенное ему в ячейке место:

Этот контейнер используется куда реже чем Grid, но и он тоже нужен – например с его помощью можно создать калькулятор, изменяющий размер кнопок или аналог сапера.

Stack (стек)

Контейнер Stack тоже жестко управляет положением помещенных в него элементов. Он складывает их в стопку, обычно по вертикали в направлении сверху вниз. При этом каждый из элементов занимает по вертикали столько места, сколько ему нужно, а по горизонтали ограничен размерами стека и выравнивается по его краю:

20

Новые возможности Visual Studio 2008

Стек очень удобно использовать в тех случаях, когда несколько элементов управления нужно разместить один за другим с равными интервалами.

WrapPanel

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

Как видно, каждый из контейнеров реализует довольно-таки простую стратегию размещения элементов внутри себя, и каждого из них по отдельности недостаточно для создания полноценного интерфейса. На практике, каждое окно обычно использует довольно-таки много контейнеров вложенных друг в друга. Как правило, контейнером верхнего уровня выступает Grid, в которую могут быть вложены StackPanel и WrapPanel.

Всплывающие события и зависимые свойства

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

Silverlight

В самом начале этого раздела я написал, что WPF предназначен для разработки полнофункциональных оконных приложений. На самом деле это не совсем так. WPF может использоваться и для разработки Web-приложений. Правда для того, чтобы такое приложение заработало, нужно, чтобы в браузере пользователя был установлен специальный плагин – Silverlight, который выступает в роли исполняющей среды для приложений WPF.

Silverlight поддерживает далеко не все элементы управления, входящие в WPF и далеко не все теги XAML. Набор элементов управления, работающих в Silverlight называется WPF/E, где E расшифровывается как Everywhere. Silverlight доступен не только под Windows и не только в Internet Explorer – он работает также и в FireFox и даже в Firefox по линуксом.