Pull to refresh

Comments 91

Три замечания.

Замечание первое.
Явная имплементация всегда статична. Она не может быть переписана (override) или перекрыта (new) в классах-потомках.
Не вполне верно. Явная имплементация по сути может быть переписана в классе-потомке, если тот же самый интерфейс реализовать еще раз. При этом используется другой механизм, нежели override, но результат аналогичен.
interface A { void Foo(); }
class B : A { void A.Foo() {} } // Явная реализация
class C : B, A { void A.Foo() {} } // Аналог overide в виде еще одной явной реализации


Замечание второе.
Явная имплементация всегда приватна (private).
В C# — да, явная реализация всегда приватна (точнее, она всегда private virtual sealed). Но статья все-таки называется «Немного об интерфейсах в .Net» — а в других языках этого семейства это не так. Я говорю о MSIL и C++/CLI. В этих языках при реализации интерфейса реализующий метод может иметь любой модификатор доступа и имя:
public interface class A { 
	void Foo(); 
};

public ref class B : A {
protected:
	virtual void Bar() = A::Foo {}
};

Кстати, это — еще один контрпример к первому замечанию.

Замечание третье:
для foreach необходима имплементация не IList, а IEnumerable
Неверно. Для foreach необходимо наличие открытого метода GetEnumerator в классе или в одном из интерфейсов, возвращающего объект или структуру, у которого есть открытые метод bool MoveNext() и доступное для чтения свойство Current. Этот механизм позволяет в некоторых случаях (например, при итерации списков) создавать итератор не в куче, а на стеке.
    struct EmptyEnumerable
    {
        public EmptyEnumerator GetEnumerator() { return new EmptyEnumerator(); }
    }

    struct EmptyEnumerator
    {
        public bool MoveNext() { return false; }
        public object Current { get { throw new InvalidOperationException(); } }
    }

    class Test
    {
        public void Run()
        {
            foreach (var q in new EmptyEnumerable())
                throw new InvalidOperationException();
            Console.WriteLine("Hello, world!");
        }
    }
Для foreach необходимо наличие открытого метода GetEnumerator в классе или в одном из интерфейсов, возвращающего объект или структуру, у которого есть открытые метод bool MoveNext() и доступное для чтения свойство Current. Этот механизм позволяет в некоторых случаях (например, при итерации списков) создавать итератор не в куче, а на стеке.

т.е. при использовании foreach происходит не приведение объекта к интерфейсу IEnumerable, а поиск метода GetEnumerator с помощью рефлексии?
Нет. Происходит поиск метода GetEnumerator на этапе компиляции.
Попробуйте скомпилировать тот код, который я привел, а потом декомпилировать в ildasm, если не понятно что происходит.
Поддержу человека. Зря минусов ему наставили, потому что игнорирование наследования от интерфейса действительно вводит в ступор. А на счет создания итератора в стеке, так никто наследования структур от интерфейсов не отменял. Приведенный ниже пример работает отлично. Ну а то, что он без интерфейса может работать, так это, как по мне, большой недочет.

public struct Iterator: IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        for (int i = 0; i < 10; i++)
        {
            yield return i;
        }
    }
}
обалдеть, еще и заминусовать кто-то успел)я уже отвык от того, что тут минусы лепят просто так
Вот разбор 4х вариантов генерации кода для ForEach
Интерфейсы всегда имеют ссылочный тип. Поэтому в данном случае реализовывать интерфейс в структуре имеет мало смысла. Потому что если бы foreach умел работать только интерфейсом, то все равно пришлось бы осуществлять операцию упаковки для этой самой структуры. И не существовало бы способа обойтись без упаковки.
Спасибо за очень интересные замечания!
По форме — англицизмы раздражают, да ещё и приправленные тавтологией — имплементирован/имплементирует/имплементация, генерический вариант и т.п.

По сути — интересное исследование.
Спасибо за замечание и за оценку!

Я, к сожалению, очень редко читаю техническую литературу на русском языке и мне сложно подбирать термины. Как было бы Вы написали «имплементация» по-русски? И разве по-русски нет выражения «генерические классы»? Я постараюсь исправить статью, если действительно здесь есть хороший русский термин.
Имплементация это почти всегда реализация.
Спасибо!
Если у Вас есть еще какие-то предложения по улучшению русского языка, я с радостью постараюсь и их учесть.
Устоявшиеся русские термины:
«Реализация [интерфейса]»
«Обобщенные классы»
О, спасибо, поправлю статью когда доберусь до работы!
Я уже называл причину, по которой «шаблонные» — не лучший перевод
Managed C++ это извращение, которое обычно служит для связки модулей, а не является главным языком проекта, где обычно пишут сложные классы. Вот и наблюдаем коллизии реализаций одного и того же подхода, пришедших из разных миров. Но ведь обе эти реализации по сути предназначены для решения одной задачи, поэтому приём, независимо от реализации, можно обозвать одним словом (с уточнением в редких случаях, когда есть опасность перепутать).
>Как было бы Вы написали «имплементация» по-русски?

На мой взгляд вполне нормальное слово, но есть еще «реализация»

>И разве по-русски нет выражения «генерические классы»?
Такого вообще никогда не слышал :) Или «дженерики» или «обобщения»

— P.S. Я буду обновлять страницу перед отсылкой комментария
Дженерики? Вы уверены? Для меня это слово совсем не звучит по-русски…
А это и не по-русски, просто ближе к названию на английском)
имплементация — реализация
генерические — обобщенные
инстанция (это вообще ужас) — экземпляр

Я тоже редко читаю на русском (и использую слово имплементация в устной речи), но если пишешь для русской аудитории, лучше писать корректным языком, как мне кажется.

UPD: не обновил страницу, быстро написали. «Дженерики» тоже использую в устной речи, английские слова всегда почти лучше кальки с них
Да, но у меня проблема заключается в том, что я никогда не общаюсь с русской аудиторией.
Я начал изучать информатику в Германии и здесь же закончил университет, поэтому я знаю все термины либо как они были в книгах (на английском), либо как их использовали в своей речи немецкие профессора. Поэтому для меня Implementation, Instanz, Generische Klassen — вполне нормальные слова и выражения. Поэтому я охотно исправляю на более принятые в русском языке термины.
Я тоже редко читаю на русском (и использую слово имплементация в устной речи), но если пишешь для русской аудитории, лучше писать корректным языком, как мне кажется.

Настолько привык к английским терминам, что на русском часто путаюсь. Например, переопределение/перекрытие/перегрузка/сокрытие — что из этого override, что new?
Я бы сказал переопределение — override, перегрузка — overload, перекрытие — та же сигнатура без new, сокрытие — та же сигнатура с new. Последние два, впрочем, сложно различить. Но это скорее из-за того, что обычно new не используется, т.к. плохой стиль и скорее как редкий воркэраунд используется.
>>И разве по-русски нет выражения «генерические классы»
В русских текстах часто пишут «Generic-классы». Термин «обобщённые классы» не все сходу воспринимают, а слово «генерические» действительно режет глаз. Поэтому разумно оставить соответствующий термин без перевода — и так все поймут что имеется ввиду.
Имплементация = реализация, например.
По-русски «генерические»? Типичный англицизм от «generic», варианты перевода на данный момент — шаблонный класс, класс-шаблон, обобщенный класс; интересная дискуссия на эту тему есть здесь, в последнем посте (от 2004 г.) есть и ваш вариант в качестве… шутки =)
Microsoft использует перевод «универсальный класс», тоже вполне годный вариант.
Вариант «шаблонный класс» лучше не использовать, поскольку в C++/CLI существуют как шаблонные (template), так и обобщенные (generic) классы.
Пожалуй, правильнее всего использовать вариант Microsoft, хотя бы для того, чтобы тебя понимали те, кто читает MSDN на русском. Для тех, кто предпочитает оригинальный MSDN, в скобках указывать оригинальное выражение. В идеале же, после написания статьи, неплохо бы сделать список/словарь многократно использованных переведенных терминов и поместить в начало статьи. Что типа такого —

Универсальный класс — generic class;
Уиниверсальный тип — generic type;


Наверное, такой подход был бы максимально удобен читателю.
Лично мне бы во время чтения мешали лишние упоминания в скобках. А список терминов в конце статьи помог бы мало — я же её уже прочел. Прыгать туда-сюда во время чтения не очень хорошо
В комментарии написано — и поместить в начало статьи.
Пожалуй, правильнее всего использовать вариант Microsoft

Мелкомягких за их автоматические переводы иногда хочется взять и у**ать. Они такие хитровы**анные термины выдумывают, что в жизни не догадаешься, что имелось в виду. Чего стоит перевод attached properties, например… Если в статье будут употребляться мелкомягкие «термины», то я её читать смогу с большим трудом.
Меня тоже немного покоробило.
У нас для интерфейсов используется «Реализация», для Generic — «Обобщенные» или «Типизированные».

P.S.:
К нам пришел новый тестировщик, и каждый раз когда он говорит что-то вроде «Бага репродьюсится» — так и хочется запустить в него чем-нибудь тяжелым.
Давайте все же не будем придираться к словам. Ну написал человек как написал, все же прекрасно поняли смысл текста. Между прочим, тут часто встречаются фразы в стиле: «Полазив по сайтам, появилась идея...». Это в принципе неграмотно, т.к. отсутствует связь между частями предложения. Правильный вариант «Полазив по сайтам, мы придумали...». Ну и т.п.
Спасибо за тёплые слова!
Поскольку у меня с русским языком, как у многих «технарей», еще со школы было все просто ужасно, я рад что по крайней мере меня понимают и буду стараться расширять свой словарный запас и русской технической лексикой.
Ну здесь же речь о терминологии, а не о русском языке. И на специализированном сайте это весьма актуальная проблема.
Ничего личного против автора не имею, сама статья дала пищу для размышлений.
Спасибо за статью — хотя я бы сделал ещё упор немного на другое:
Для меня тоже было откровением, что array реализует интерфейс IList. Ведь IList — это в первую очередь методы Add() и Remove(). Поэтому логично было ответить, что array не реализует интерфейс IList.
Однако чуда не произошло: хоть массив и реализует этот интерфейс — все методы списка (Add(), Clear(), Remove(), RemoveAt(), Insert()) им не поддерживаются.
Так что не понятно, зачем такая реализация вообще нужна. Если я передаю в метод параметр типа IList<>, то очевидно, что я хочу в этом методе использовать методы специфичные именно для этого интерфейса (в противном случае я бы использовал параметр IEnumerable<>). А так получается есть возможность без проверки передать массив в качестве такого параметра, чтобы потом в рантайме вылезла ошибка NotSupportedException.
IList — это в первую очередь доступ к элементам по индексу, и массив его поддерживает.

А те методы, которые вы перечислили — это методы списка переменного размера, которым массив не является.

Загляните в документацию (в msdn, например):
Представляет коллекцию объектов, доступ к которым может быть получен отдельно, по индексу.
Где здесь вообще сказано про изменяемость? Все изменяющие методы — опциональны.
Да, Вы не первый, у кого возникает такой вопрос — у меня он тоже возник в процессе моего исследования.
Лучше всего на него, по-моему, отвечает John Skeet в одном из своих ответов на StackOverflow. Также мне интересным показался и вот этот вопрос, и особенно статья, на которую там дана ссылка.
Советую почитать C# in Depth за авторством Джона Скита. Там очень многие вопросы из приведенных в статье\комментариях рассматриваются и ваш конкретно расписан очень подробно.
Спасибо!
Я начинал читать её, но так и не осилил до конца. Мне очень нравится его стиль изложения, когда один и то же код постепенно улучшается с каждой версией C#.
UFO just landed and posted this here
Спасибо, буду считать это комплиментом и надеяться дорасти когда-нибудь до таких корифеев, как Рихтер или Трёлсен. :)
Желаю удачи в профессиональном росте! Только имейте ввиду, что «Troelsen» в русском варианте пишется как «Троелсен».
Хм… может быть это американское прочтение его фамилии?
Спасибо за замечание!
Советую задумался о заучивании книги «CLR via C#» Джеффри Рихтера. Там все эти вопросы и еще много много других освещены подробно.
Если бы Рихтер писал стихами, то я бы с удовольствием их заучил :)
А так мне каждый раз приходится заново перечитывать, потому что к середине книге я уже напрочь забыл все эти особенности поиска и подгрузки assembly, что он рассматривает в первой и второй главе…
Вообще mayorovp все уже отметил, но вот обычно в реализациях компилятора, когда он встречает forach и мы имеем массив, то там идет оптимизация и на выходе мы будем иметь for (var i = 0; i < array.Length; i++). Такая оптимизация есть и в Mono и в Microsoft .NET. Поэтому, в большинстве случаев, вызова GetEnumerator у массива не будет.
В книге C# in Depth, которую я упоминал выше, так написано, считаю надежным источником. К тому же какие проблемы скомпилить foreach для int[] и List<int> и посмотреть дизассемблированный код? Я проверил — в IL для первого случая вызовов GetEnumerator и проч. нету, для второго — есть.
Да я-то скомпилировал, и знаю это. Просто нужен какой-нибудь компетентный источник, чтоб тыкать людей туда носом.
А тут не нужны источнки, скачайте моно. И скомпилируйте 2 цикла с foreach и for, и посмотрите IL код. Тело цикла будет совпадать в обоих случаях (также и для Microsoft.NET). Конечно в блоге разработчиков где-то есть описание но искать лень.
Допустим у нас есть следующий код:

using System;
using System.Linq;

namespace TestArrayInForeach
{
    class Program
    {
        static void Main(string[] args)
        {
            var array = args.Select(int.Parse).ToArray();
            var arrayLength = array.Length;

            // First case
            for (var index = 0; index < array.Length; index++)
            {
                Console.WriteLine(array[index]);
            }

            // Second case
            foreach (int item in array)
            {
                Console.WriteLine(item);
            }

            // Third case
            for (var index = 0; index < arrayLength; index++)
            {
                Console.WriteLine(array[index]);
            }
        }
    }
}


Скомпилируем его:

Первый цикл:
            {
                Console.WriteLine(array[index]);
00000092  mov         eax,dword ptr [ebp-8] 
00000095  mov         edx,dword ptr [ebp-1Ch] 
00000098  cmp         eax,dword ptr [edx+4] 
0000009b  jb          000000A2 
0000009d  call        79033254 
000000a2  mov         ecx,dword ptr [edx+eax*4+8] 
000000a6  call        048F4D90 
            for (var index = 0; index < array.Length; index++)
000000ab  inc         dword ptr [ebp-8] 
000000ae  mov         eax,dword ptr [ebp-8] 
000000b1  mov         edx,dword ptr [ebp-1Ch] 
000000b4  cmp         eax,dword ptr [edx+4] 
000000b7  jl          00000092 


Второй цикл:
000000c7  mov         eax,dword ptr [ebp-14h] 
000000ca  mov         edx,dword ptr [ebp-20h] 
000000cd  cmp         eax,dword ptr [edx+4] 
000000d0  jb          000000D7 
000000d2  call        79033254 
000000d7  mov         eax,dword ptr [edx+eax*4+8] 
000000db  mov         dword ptr [ebp-0Ch],eax 
            {
                Console.WriteLine(item);
000000de  mov         ecx,dword ptr [ebp-0Ch] 
000000e1  call        048F4D90 
000000e6  inc         dword ptr [ebp-14h] 
            foreach (int item in array)
000000e9  mov         eax,dword ptr [ebp-14h] 
000000ec  mov         edx,dword ptr [ebp-20h] 
000000ef  cmp         eax,dword ptr [edx+4] 
000000f2  jl          000000C7 
            }


Третий цикл:
            {
                Console.WriteLine(array[index]);
000000fc  mov         eax,dword ptr [ebp-10h] 
000000ff  mov         edx,dword ptr [ebp-1Ch] 
00000102  cmp         eax,dword ptr [edx+4] 
00000105  jb          0000010C 
00000107  call        79033254 
0000010c  mov         ecx,dword ptr [edx+eax*4+8] 
00000110  call        048F4D90 
            for (var index = 0; index < arrayLength; index++)
00000115  inc         dword ptr [ebp-10h] 
00000118  mov         eax,dword ptr [ebp-10h] 
0000011b  cmp         eax,dword ptr [ebp-4] 
0000011e  jl          000000FC 
Подозреваю что там всё хитро. Не влезая в IL провел несколько тестов производительности For и Foreach.
Оказалось следующее: если скомпилировать в Release следующий код:
int s = 0;
for (int z1 = 0; z1 < array.Lenght; z1++)
{
	s += array[z1];
}
и
foreach (var i in array)
{
	s += i;
}

то время выполнения идентично (гуляет на 0.3%).

если же написать так:

int c = array.Length;
for (int z1 = 0; z1 < c; z1++)
{
	s += array[z1];
}

то код стабильно выполняется быстрее, чем foreach, где-то на 3%.
Провел небольшое исследование…
Код до компиляции:
            foreach (var y in x)
                Console.WriteLine(y);


Код после декомпиляции:
	int[] array = x;
	for (int i = 0; i < array.Length; i++)
	{
		int y = array[i];
		Console.WriteLine(y);
	}


Действительно, компилятор C# считает длину массива переменной…
Он так не считает, просто его не правильно понимаете, он не будет каждый раз дёргать метод array.get_Length
Тесты, приведенные выше, показали — если сохранить длину массива в переменную и использовать в цикле for значение переменной, а не array.Lenght, то такой код стабильно на 3% быстрее. Мелочь, но всё же…
Да и как это, спрашивается, for не будет каждый раз дергать — попробуйте в цикле поменять значение второй переменной из цикла for — на каждом цикле значение этой переменной вычисляется заново.
Я так думаю, что если у вас в цикле что-нибудь более-менее комплексное, то разница будет неощутима. Если же цикл легкий и короткий, то в некоторых случаях его можно переписать на LINQ для читабельности и меньшей вероятности ошибиться в индексах. В большинстве случаев от C# не требуется сверхвысокой производительности и такие вещи как вынос Length в переменную могут только затруднить чтение кода.
Так ведь вопрос не в этом. Дело в том, что если уж компилятор автоматически преобразует foreach в for, то надо было бы делать это оптимально — ведь именно оптимальности ждут от оптимизаций.
Это с одной стороны. А с другой будет увеличение времени компиляции и размера сборки. Как мне кажется, все сделано правильно. Хочешь мегапроизводительность в мелочах — пиши в стиле C. А если нет — используй foreach. Зачем все усложнять?
А с другой будет увеличение времени компиляции и размера сборки.

Где увеличение? Как?!

Увеличение на 10 байт и 100 наносекунд (беру с сильным запасом) — это так много?
Где-то столько же вы и выиграете в ран-тайме. А на генерацию локальной переменной по идее все-таки время нужно, нужно же обеспечить уникальность имени — значит, нужно проверять область видимости. Если использовать недопустимые для языка символы в идентификаторе (но допустимые для IL) — тогда нужно проверять и уникальность на случай если несколько циклов в одной области видимости. И даже если их генерировать с циферкой на конце без проверок — на генерацию потребуется время как ни крути и, я так подозреваю, больше, чем 100 наносекунд. Плюс этот код вносит сложность в дальнейшую разработку компилятора. На написание, отладку и тестирование нужно время. Не факт, что руководящие одобрили бы затрату ресурсов на такие мелочи.

Вы не думайте, что я не согласен с тем, что можно было бы сделать как вы предлагаете, я лишь говорю что нет ничего страшного в том, что так сделано не было. Я считаю, они знали о такой возможности, но по своим причинам пошли по пути наименьшего сопротивления.
Вероятно вы запускали в дебаге или он был подключен? Что получается из трех ваших циклов можете посмотреть выше
1. Цитата из моего комментария «если скомпилировать в Release следующий код». Само собой, что в DEBUG и под отладкой цифры совсем другие.
2. Внимательнее посмотрите код циклов — они все 3 разные.
2.1 Цикл for от 0 до array.Lenght
2.2 Цикл foreach по элементам array
2.3 Цикл for от 0 до значения локальной переменной, в которую предварительно записали значение array.Lenght

а Выше показан результат декомпиляции 1 foreach и двух идентичных циклов for
Если внимательно посмотреть третий цикл, то видно, что for (var index = 0; index < arrayLength; index++), в переменной arrayLength нет точки. Переменная создается до цикла.

Во вторых там я хотел показать именно в том примере, что циклы используют индекс, а не через GetEnumerator, поэтому и запостил там, а не тут. Ну и разницы между двумя for вообще никакой нет. Да, foreach немного по другому скомпилировалось на 6 байт больше в том случае, но это ни разу не показатель в таких синтетических тестах.
Единственным способом выявления таких реализаций является использование рефлексии, например при помощи Object Browser в Visual Studio.

По-моему проще посмотреть в MSDN )
Ну это сработает только для стандартных классов .Net.
А для решения этой проблемы «в общем» приходится использовать какой-то тул с поддержкой рефлексии, самый простой из них — Object Browser.
Самый простой — посмотреть исходный код. Если его нет — студия восстановит что сможет из метаданных. Получается гораздо нагляднее, чем Object Browser. По крайней мере, для меня.
Понятно, что исходный код — самое лучшее из решений. Но для обзора API все-таки лучше подходят какие-то инструменты вроде Reflector.Net.

А по поводу восстановление класса из метаданных — я не совсем уверен, что это работает во всех версиях…
Класс из метаданных не восстановится никак.

Но у .NET открыты исходники. И студия умеет их выкачивать и показывать.

А «инструменты вроде Reflector.Net.» — это декомпиляторы.
А мне не нужно восстанавливать весь класс — мне достаточно восстановить сигнатуры классов и методов, а для этого метаинформации более чем достаточно.

Я знаком с возможностями VS по подгрузке исходных кодов .Net, но это, как я отметил выше, не универсальное решение.
Всегда удивляло, зачем работодатели требуют знания таких вещей наизусть когда есть документация. Разве что на случай если Интернет отключат… Можно, конечно, использовать как косвенный индикатор опыта, но по-моему единственная проверка, что имеющая смысл при найме программиста — пара практических тестовых заданий (например на реализацию и на дебаг) с последующим анализом качества кода, при этом как и в реальных условиях у человека в «дано» должна быть полноценная IDE (в случае C# — актуальная VisualStudio) и свободный доступ в Интернет. Если только это не набор программистов в экипаж, отправляющийся на Марс…
Массивы и цикл foreach — это базовые части языка, их нужно знать без документации.
Ну это было второе (из трех) собеседований, причем оно состояло из трех частей:
1. Письменного теста (вот оттуда этот пример).
2. Теста на компьютере (написать в Visual Studio сначала набор unit tests для метода, рассчитывающего медиану, а потом и запрограммировать этот метод).
3. Устное собеседование с двумя Senior Developers.

Каждая часть длилась по часу. По мне — так вполне адекватно для первой части: проверки на «вшивость», знаешь ли ты вообще, что такое цикл и .Net.
Т.е. если неправильно ответить на этот вопрос, то ты не знаешь, что такое цикл? :)
Я точно знаю и использовал foreach для int[], но не задумывался, реализует ли он IList.
Ну, там было в тесте три разных раздела (базовые вещи, .Net и базы данных), в каждом по 30-35 вопросов и все на один час. Я не думаю, что неправильный ответ тут сразу бы привел к дисквалификации, скорее вызвал бы удивление, если все остальное было бы неправильно.

В общем и целом я думаю, учитывается совокупность факторов, и тот факт, что все это делается в стрессовых условиях.
Покажусь снобом, или ещё кем-нибудь плохим, но не зная про требование foreach к классу, про массив и IList, про то где находится Count, вам не стоило упоминать про Senior .Net Developer. Это просто смешно. Такие вещи знают обычно Junior'ы с годом опыта которые прочли пару книжек по C#/.NET

О, как я ждал этого комментария! :)

Действительно, Вы покажетесь снобом или «кем-нибудь плохим», особенно учитывая тот факт, что на работу меня взяли (да, именно Senior Developer).

Вы, конечно, это тоже все знали после года работы Junior и прочтения «пары книжек» по .Net, не так ли?

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

PS: сколько Senior .NET Developer зарабатывает в Германии?
И что же Вы знали?

.NET Senior Developer зарабатывает столько, на сколько он сможет договориться :)
> Знал то, про что выше написал GrigoryPerepechko.

Ну, то что он выше написал я тоже, честно говоря, не понял — что конкретно он имеет в виду. Поэтому мне было интересно, что конкретно Вы знали, разрабатывая just4fun.

Я спрашиваю потому, что я собственно не только работаю много лет как разработчик, я и преподаю .Net как MCT и те же Collections входят в базовый курс .Net (раньше экзамен 70-536), который я преподавал несколько раз как тренер. Но утверждать, что это всё знают junior и в рамках собеседования легко ответят не все это, особенно в той патетичной манере, как это изложено — честно скажу, не верю. Как говорится «на миру и смерть красна» — легко заявлять о чем-то, когда ты сидишь в браузере и печатаешь комментарий…

По поводу зарплат — что Вас не устраивает? В Германии нет тарифной сетки для работающих в частных структурах, поэтому зарплата Senior Developer полностью зависит от того, насколько он договорится с работодателем — и эта зарплата может колебаться в 2-3 раза в зависимости от фирмы, земли и места работы…
По поводу зарплат — что Вас не устраивает? В Германии нет тарифной сетки для работающих в частных структурах, поэтому зарплата Senior Developer полностью зависит от того, насколько он договорится с работодателем — и эта зарплата может колебаться в 2-3 раза в зависимости от фирмы, земли и места работы…
От земли — может быть. Остальное ерунда, т.к. зарплата полностью подчиняется закону рынка. И, таким образом, при условии, что «Senior Developer» это некий стандарт, то все такие программисты со временем будут работать в одном месте — там где зарплата в 3 раза выше. Либо везде будет примерно одинаковая зарплата.

я и преподаю .Net как MCT
Тогда я не понимаю, как вы могли не знать, что Array реализует IList`1, что Count это свойство ICollection`1 и ICollection, и про explicit interface implementation? — Про это и пишет GrigoryPerepechko
> Остальное ерунда, т.к. зарплата полностью подчиняется закону рынка. И, таким образом, при условии, что «Senior Developer» это некий стандарт, то все такие программисты со временем будут работать в одном месте — там где зарплата в 3 раза выше.

А уже несколько раз пытаюсь Вам сказать, что это — не «некий стандарт», это скорее описание позиции, где (практически) с первого дня ожидается работа над продуктивным кодом и где никто не настроен сначала тебя обучать тем технологиям, с которыми нужно работать. Поэтому Senior Developer получает столько, насколько он договорился в интервью. Неужели Вы никогда не видели вакансии «зарплата по результатам собеседования»?

>Тогда я не понимаю, как вы могли не знать, что Array реализует IList`1, что Count это свойство ICollection`1 и ICollection, и про explicit interface implementation?
Наверное если бы меня целенаправленно спросили об этом, я бы подумав восстановил всю структуру наследования коллекций и их интерфейсов в голове — но вопрос был поставлен по-другому и это был один из 90 (!) вопросов на которые я должен был ответить в течение часа.
Тем не менее, даже формально когда-то заучив это для сертификаций и преподавания я, честно признаюсь, не понимал что речь в данном случае идет о explicit implementation, и именно осознание этого факта показалось мне достаточно интересным для статьи.
Вы же утверждаете, что для Вас это все было обыденным знанием и все наследование Вы всегда можете его воспроизвести «от зубов»?
Вы же утверждаете, что для Вас это все было обыденным знанием и все наследование Вы всегда можете его воспроизвести «от зубов»?
Я этого нигде не утверждал.
Тем не менее, даже формально когда-то заучив это для сертификаций и преподавания я, честно признаюсь, не понимал что речь в данном случае идет о explicit implementation, и именно осознание этого факта показалось мне достаточно интересным для статьи.
Это элементарно, Ватсон.

1. То что Array реализует IList`1 _должно_ быть известно всем, т.к. с массивами и всякими списками все работают постоянно.
2. У IList`1 где-то в иерархии есть поле Count.
3. У Array этого поля нет.
4. Следовательно: оно было реализовано через explicit implementation, т.к. нельзя оставлять члены интерфейса без реализации.

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

Неужели Вы никогда не видели вакансии «зарплата по результатам собеседования»?
Не знаю как у вас в Германии, а в России за этой фразой обычно прячут неприлично низкую зарплату. У кого высокие зарплаты — не скрывают суммы.

А уже несколько раз пытаюсь Вам сказать, что это — не «некий стандарт», это скорее описание позиции, где (практически) с первого дня ожидается работа над продуктивным кодом и где никто не настроен сначала тебя обучать тем технологиям, с которыми нужно работать.
Поэтому Senior Developer получает столько, насколько он договорился в интервью.
1. Я прекарсно осознаю что значит Senior .NET Developer. 2. Между первой и второй фразой нет никакой связи. 3. Работодатель заранее знает, сколько он будет платить кандидату — так называемая «вилка».
Просвятите, как это сакральное знание помогает сеньору в работе?
Изящный код и красивые архитектуры часто являются результатом применения вот таких вот сакральных знаний. Умея, к примеру, Java на уровне «senior» вы можете писать хороший код на C#, но это будет C# код Java программиста. Мелочи позволяют использовать язык по максимуму.
>Чтобы получить доступ к имплементации необходимо кастовать инстанцию класса к интерфейсу
доставило.
Я рад, что смог доставить Вам что-то… надеюсь удовольствие… :)
Должны были бы знать по фильму, что заклинания из чёрной книги читать нельзя… ;-)
Sign up to leave a comment.

Articles