danilaxxl danilaxxl

CollectableItemData.cs

[CreateMenuItem(fileName = "newItem", menuName = "Data/Items/Collectable", order = 51]

GoloGames GoloGames

vadya_ivan, рад, что вам игра показалась интересной : )

P.S. Кстати уже доступна бесплатная демо-версия в Steam

vadya_ivan vadya_ivan

Визуал, задумка, музыка , механики, все в цель

GoloGames GoloGames

Ato_Ome, спасибо за позитивные эмоции, будем стараться : )

Ato_Ome Ato_Ome

Потрясающий результат, все так четенько, плавненько)
То ли саунд, то ли плавность напомнили мне игрушку World of Goo, удачи вам в разработке и сил побольше дойти до релиза!)

Cute Fox Cute Fox

Graphics are a little cool, good HD content. But this game doesn't cause nary interest me.
However the game is well done.

GMSD3D GMSD3D

Почему действие после всех условий выполняется?
[step another object]

Zemlaynin Zemlaynin

Jusper, Везде, но наугад строить смысла нет. Нужно разведать сперва территорию на наличие ресурсов.

Jusper Jusper

Zemlaynin, а карьеры можно будет везде запихать?
Или под них "особые" зоны будут?

Zemlaynin Zemlaynin

Это так скажем тестовое строительство, а так да у города будет зона влияния которую нужно будет расширять.

Jusper Jusper

А ссылка есть?

Jusper Jusper

Я не оч понял из скриншота, как вообще работает стройка. У игрока будет как бы поле строительства?

split97 split97

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

split97 split97

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

ViktorJaguar ViktorJaguar

Почему я нигде не могу найти нормальный туториал, где покажут как экипировать предмет (например, меч) в определенную (выделенную под оружие) ячейку???

Логотип проекта Программирование

Методы расширений

Если вы находитесь на стадии обучения языку C# - данная статья как раз для вас! Она расскажет вам о том, как использовать одну из синтаксических фич языка - методы расширений.

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

Пример

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

var last = 0;
if (array != null && array.Count != 0) 
    last = array[array.Length - 1];

В результате в переменной last хранится либо последний элемент массива, либо, если массив оказался пустым - хранится 0, как значение по умолчанию.
Вы долго и муторно вбиваете этот код при каждом использовании, пока однажды к вам в голову не приходит идея - "а почему бы мне не выделить написанное в отдельный метод"?
И вы создаете метод на подобии вот такого:

public static class IntArrayUtils 
{
    public static int GetLast(int[] array) 
    {
        var last = 0;
        if (array != null && array.Length != 0)
             last = array[array.Length - 1];
        return last;
    }
}

Который вызывается в коде вот так:

var last = IntArrayUtils.GetLast(array);

Согласитесь, уже короче? Но чего -то не хватает. Может, красоты?
Вот тут мы и подошли к методам расширений. Согласитесь, что вот так было бы лучше:

var last = array.GetLast();

Нам бы не пришлось указывать странный метод IntArrayUtils и данный метод выглядел бы в точь-в-точь как родной метод у int[], который, к сожалению мы добавить не можем.

Решение

Что же нужно изменить чтобы достичь такого результата? Все очень просто, к существующему методу мы добавим всего одно слово this перед первым параметром:

public static class IntArrayUtils 
{
    public static int GetLast(this int[] array) 
    {
        var last = 0;
        if (array != null && array.Length != 0)
             last = array[array.Length - 1];
        return last;
    }
}

В результате все экземпляры int[] в нашем коде обзавелись дополнительным методом GetLast().
Ваш код после такой простой манипуляции может вызываться двумя разными способами, которые я уже указывал выше.
Как стандартным, через статический класс:

var last = IntArrayUtils.GetLast(array);

Так и через экземпляр:

var last = array.GetLast();

Заметки

Немного тонкостей о методах расширений:

  • Метод расширения обязан быть частью static класса
  • Несмотря на то, что метод выглядит как метод экземпляра, он является статичным и при компиляции подменяется. Это обязывает вас делать проверку на null, чтобы не допустить ошибок.
  • Метод может иметь собственные Generic типы, как и любой другой метод. Это позволит вам сделать код более универсальным.
  • Увы, вы не сможете расширить этим статические классы, например добавить новый статический метод в класс Math. Только экземпляры, только хардкор.

Примеры использования

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

Код желательно обустраивать так, чтобы его было легко читать. Например:

text.SaveAsFile(file);

Среди стандартных библиотек данный способ используется для всех методов у массивов и перечислителей - методы ForEach, Select, Any, Where, Contains и так далее.
Очевидно, что подобное решение отлично подходит для случаев, в которые просто так метод не добавишь, либо этот метод весьма специфичен и требует возможностей static классов. Например, к таким относится удаление объекта, где обычный метод экземпляра не может обнулить экземпляр, через this = null потому что сам является его частью.

Несмотря на плюсы, не стоит использовать методы расширений если:

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

Общепринято складывать методы расширений в отдельный класс (без обычных статических методов) и дописывать данному классу слово Extensions. Например, класс, содержащий методы расширений для типа List обычно называют ExtensionsList или ListExtensions. Это не обязывает вас делать тоже самое, но насколько я видел в проектах всегда есть отдельная папочка для методов расширений с вот такими классами.

Вот наверное и вся основная информация по методам расширений. Спасибо за внимание.

Смотрите также:


Комментарии



prog:

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

это плюсы, а разные решения. Как бы расширение для этого и не нужно.
prog:

которые есть у существующего метода расширения?", "а как себя ведут методы расширения при наследовании?" и другие.

тут уже пошла легкая наркомания, похоже ты не докурил основы. 1) Ошибка компиляции, проблема в расширении 2) Ошибка компиляции, какое нахрен наследование у статического класса
prog:

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

Для коллекций тоже иногда нужны расширения под нужды системы. Вот например у нас в проекте используется Join для коллекций, и еще много различных методов для быстрого преобразования и агрегирования данных. уть методов расширения обернуть большую цепочку повторяющихся действий в один метод. Еще один пример, есть UIHelper который помогает создавать UI часть для приложения, в нем храняться нужные контексты и базовые методы, аля DisplayFor и т.д. Для создания простых форм для редактирования данных приходится писать

UI.BeginGroupFor(model => model.ExtensionName);
UI.LabelFor(model => model.ExtensionName, Localization.ExtensionEditor_ExtensionName);
UI.EditorFor(model => model.ExtensionName);
UI.ValidationMessageFor(model => model.ExtensionName);
UI.EndGroup();

Делаем расширяющий метод, и вуаля

UI.GroupFor(model => model.ExtensionName, Localization.ExtensionEditor_ExtensionName);

все просто, красиво и главное потом легко изменить отображение всех форм и т.д. И использовать можно на любой форме, все просто компактно и все данные всегда с собой.
prog:

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

расширение операндов дело тонкой, но в большинстве случаев весьма удобное. Но тут действительно из место внутри нормального класса.


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

java:
public final class ArrayExtension {
     public static int getLast(int[] array) {
         return array[array.size() - 1];
     }
}
...
ArrayExtension.getLast(myArray);

c#:
public static class ArrayExtension 
{
    public static int GetLast(this int[] array)
    {
        return array[array.Length - 1];
    }
}
...
myArray.GetLast();
ArrayExtension.GetLast(myArray);

По архитектуре приложений, даже я бы почитал. А Action Script мне кажется ну тема не особо приличная) Lua побольше используется и в разных играх, да и прикрутить её не сложно вроде бы.

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

Что касается вашего примера, то тут напрашивается что-то вроде класса FormPresetsFactory, инкапсулирующего в себе работу с UI-хелпером и предоставляющего N методов, каждый на отдельный вид форм плюс X protected методов для внутреннего пользования в пределах фабрики, в которых можно собрать повторяющиеся элементы форм. Почему это лучше, чем расширения? Потому что в случае необходимости легко заменить одну фабрику другой, реализующей ту-же функциональность, но совсем другими способами, а в случае с методами расширения придется не только писать дополнительные методы расширения, но и вносить изменения в существующий код, если нужно и старый вариант сохранить и новый добавить.

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

prog:

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

ну а что их сравнивать, если это они и есть?) И что тут не понятно?
prog, UI-хелпер по мне так это такая штука которая и так на высоком уровне, она работает с абстракциями и шаблонами. А делать фабрику для этого нет особо смысла, по крайней мере я не вижу в этом смысл, придется его инициализровать на каждого окна отдельно, подпихивать в UI модель, и потом использовать его. В общем вот что я скажу, на каждый проект надо смотреть по разному, в нашем случае было сделано так, никто не жаловался, в том числе и архитекторы)
Вот в случае с дата провайдерами, тут да я согласен, удобно использовать фабрику и адаптеры, чтобы можно в любой момент поменять способ получения данных.

Отличная статья, как раз намереваюсь подробнее изучать С#, так что очень кстати =)

Devion,

last = array[array.Length];

Подправь на length-1 в начале статьи

Msey, спасибо, исправил

Справка