Показаны сообщения с ярлыком c#. Показать все сообщения
Показаны сообщения с ярлыком c#. Показать все сообщения

воскресенье, 1 февраля 2015 г.

Unity 3D: Управление персонажем мышкой, вид с верху

Решаем задачу:

- Вид с верху;
- Персонаж (спрайт) должен перемещаться к месту клика мышью;
- Персонаж должен поворачиваться по направлению движения.

Решение:

using UnityEngine;

public class Player : MonoBehaviour
{

    private Vector3 _positionMove;
    private float _speed = 1f;

    private void Start()
    {
        _positionMove = transform.position;
    }

    private void Update()
    {
        UpdateInput();
        UpdateMove();
    }

    private void UpdateInput()
    {
        if (Input.GetMouseButtonDown(0))
        {
            _positionMove =
                UnityEngine.Camera.main.ScreenToWorldPoint(Input.mousePosition);
            _positionMove.z = 0;
        }
    }

    private void UpdateMove()
    {
        float moveDelta = (_positionMove - transform.position).magnitude;
        if (moveDelta <= _speed * Time.deltaTime)
        {
            transform.position = _positionMove;
            return;
        }
        Vector3 moveDir = _positionMove - transform.position;
        //
        float angle = Mathf.Atan2(moveDir.y, moveDir.x) * Mathf.Rad2Deg - 90;
        transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
        //
        moveDir.Normalize();
        transform.position += moveDir * _speed * Time.deltaTime;
    }
}

Unity 3D: FPS script

Скриптов для подсчета fps в Unity много. Способы подсчета основываются на различных исходных данных и принципах. На данный момент выбрал такой вариант:

using UnityEngine;

public class Fps : MonoBehaviour
{
    private const float UpdateInterval = 1.0f;
    private float _timeleft;
    private float _lastTime;
    private float _timeSpan;
    private int _lastFrame;
    private int _frames;
    private float _fps;
   
    void Update()
    {
        _timeleft += Time.deltaTime;
        if (_timeleft > UpdateInterval)
        {
            _timeleft -= UpdateInterval;
            _frames = Time.frameCount - _lastFrame;
            _lastFrame = Time.frameCount;
            _timeSpan = Time.realtimeSinceStartup - _lastTime;
            _lastTime = Time.realtimeSinceStartup;
            _fps = Mathf.RoundToInt(_frames/_timeSpan);
        }
    }

    void OnGUI()
    {
        GUI.Box(new Rect(10, 10, 70, 25), string.Format("fps {0}", _fps));
    }
}

среда, 13 июля 2011 г.

Компромисс в выборе между классом и структурой

На тему навела вот эта статья "NET 4.0: Class vs Struct или в чём различия между Классом и Структурой" . Давно уже собирался собрать для себя набор иерархии универсальных коллекций, которые не попадают под сборку мусора. Тема в очередной раз стала интересна после моего известного эксперимента с Windows Phone 7. Очень сомневаюсь, что моя статья будет полезна для бизнес-приложений.  Меня больше интересует данная тема для использования в XNA/DirectX и как область применения - игры.

Приглашаю вас обсудить данный вопрос.

И так, условия задачи:
- Нужна коллекция элементов для описания, например, системы частиц (это может быть все что угодно). Такая штука в которой в среднем 60 раз в секунду добавляются и удаляются сотни элементов.
- Изменения в коллекции не должны влиять на сборку мусора.
- Нельзя использовать структуры! Только ссылочные типы.
- Запрещено создавать и удалять классы, за исключением первой инициализации.
- Физическое количество элементов в коллекции должно быть постоянным. Т.к. изменение длинны массива достаточно накладная операция, а количество элементов должно постоянно плавать, то подойдет подобие кеша. Ограничение - разработчику желательно четко знать максимально возможное количество элементов в такой коллекции. А это значит игра должна быть грамотно спроектирована. Хотя лазейку я оставлю.


Набросал вот такой черновик.

Базовый класс для элемента коллекции:

public class TElementConstLength
    {
        private  int indexInCollection = -1;
        internal int IndexInCollection { get { return indexInCollection; } }
        internal void SetIndexInCollection(int i)
        {
            indexInCollection = i;
        }
    }


Базовый класс для коллекции:

public class TCollectionConstLength<T> where T : TElementConstLength, new()
    {
        private T[] elements;

        private int count = 0;
        /// <summary>
        /// Количество живых элементов
        /// </summary>
        public  int Count { get { return count; } }

        private int end = -1;

        /// <summary>
        /// Инициализация колекции
        /// </summary>
        /// <param name="length">Длинна коллекции</param>
        public TCollectionConstLength(int length)
        {
            elements = new T[length];
        }

        /// <summary>
        /// Доступ к элементу по индексу
        /// </summary>
        /// <param name="index">Индекс</param>
        /// <returns>Элемент</returns>
        public T this[int index]
        {
            get { return elements[index]; }
        }

        /// <summary>
        /// Выделение места под новый элемент
        /// </summary>
        /// <returns>Новый элемент</returns>
        public virtual T GetNew() // = Add
        {
            // если список полон, для режима отладки
            if(end == elements.Length - 1)
                Array.Resize(ref elements, elements.Length + 1);
            // 
            end++;
            count++;
            // если не инициализирован элемент
            if(elements[end] == null)
            {
                elements[end] = new T();
                elements[end].SetIndexInCollection(end);
            }
            //
            return elements[end];
        }

        /// <summary>
        /// Очистка списка
        /// </summary>
        public void Clear()
        {
            end = -1;
            count = 0;
        }

        /// <summary>
        /// Удаление элемента по индексу
        /// </summary>
        /// <param name="index">Индекс</param>
        public virtual void Remove(int index)
        {
            // выход за пределы
            if(index < 0 || index > end)
                return;
            if(index < end)
            {
                // рокировка элементов
                T temp = elements[end];
                elements[end] = elements[index];
                elements[index] = temp;
                // рокировка индексов
                int i = elements[end].IndexInCollection;
                elements[end].SetIndexInCollection(elements[index].IndexInCollection);
                elements[index].SetIndexInCollection(i);
            }
            // уменьшаем список
            end--;
            count--;
        }

        /// <summary>
        /// Удаление элемента
        /// </summary>
        /// <param name="e">Элемент</param>
        public virtual void Remove(T e)
        {
            // проверка на соответствие ссылок (защита от тупости)
            if(e != elements[e.IndexInCollection])
                return;
            Remove(e.IndexInCollection);
        }
    }

Вот пример для тестов:

using System;

namespace CollectionConstLength
{

    public class ElementTest : TElementConstLength
    {
        private string name;
        public string Name { get { return name; } set { name = value; } }
        public ElementTest() { }
    }

    class Program
    {

        static TCollectionConstLength<ElementTest> item;

        static void Main(string[] args)
        {

            item = new TCollectionConstLength<ElementTest>(5);
            ElementTest et;
            for(int i = 0; i < 5; i++)
            {
                et = item.GetNew();
                et.Name = i.ToString();
            }
            //
            Print();
            //
            item.Remove(item[1]);
            Print();
            //
            Console.ReadKey();
        }

        static void Print()
        {
            Console.WriteLine("//--------------------------------");
            for(int i = 0; i < item.Count; i++)
            {
                Console.WriteLine(
                    string.Format("index - {0}; name - \"{1}\";", 
                        item[i].IndexInCollection, 
                        item[i].Name));
            }
            Console.WriteLine("//--------------------------------");
        }
    }
}


Пример наращивания функционала через наследование:

- Расширенный класс элемента:

public class TUpdate : TElementConstLength
    {
        public virtual void Update() { }
    }

- Расширенный класс коллекции

public class TUpdateCollection<T> : TCollectionConstLength<T> where T : TUpdate, new()
    {
        public TUpdateCollection(int l) : base(l) { }
        public virtual void Update()
        {
            for(int i = 0; i < base.Count; i++)
            {
                base[i].Update();
            }
        }
    }


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

Давайте пообщаемся на эту тему.
Так же буду рад ссылкам на статьи с подобными темами и изысканиями.

суббота, 27 февраля 2010 г.

Твой путь

Данную статью я посвящаю трем категориям людей: евангелистам Microsoft, работодателям и специалистам, связавшим свою деятельность с платформой dotNet (далее будем подразумевать язык C#). На написание данного опуса меня вдохновило очередное перечитывание книги «CLR via C#» Джеффри Рихтера. Сейчас объясню почему.

На данном историческом этапе людям, позиционирующим себя как dotNet разработчики, зачастую приходится отвечать на вопросы подобные этим:

- почему вы выбрали C#, а не C++?
- чем C# лучше C++?
- а вы знаете, что C# медленнее C++? (очередной «капитан очевидность»)

Работодателей выделю в отдельный абзац, по причине существования тенденции непонимания ими основополагающих факторов. В моей практике не так давно был веселый случай, когда перспективный работодатель, зная мое владение C++, предложил работу, которая подразумевала бы мой абсолютный переход обратно на C++. На что, получив мой отрицательный ответ, мотивированный нежеланием делать шаг назад, начал дискуссию в лучших традициях “Holy wars C++ vs C#”. Но война не сложилась. Видимо сказалось мое накопленное к тому моменту недовольство с оплатой предыдущих проектов, в итоге гражданин дуется на меня до сих пор, так и не поняв мотивов и побуждений движущих мною. По этому, уважаемые работодатели, на подобные предложения я хотел бы ответить Вам цитированием слов абсолютного авторитета в нашей области Джеффри Рихтера (цитата приведена далее по тексту и выделена курсивом, особенно ценное выражение выделено жирным курсивом). И если в ответе на ваше предложение вы нашли ссылку на данную статью, то мой ответ скорее отрицательный, чем положительный.

Так же постоянно находятся желающие, хотите вы этого или нет, поспорить с вами, развязать священную войну и доказать что именно C++ есть единственный и ничего кроме него. От серьезного человека Вы вряд ли такое услышите и скорее всего найдутся более интересные темы для беседы с ним. Разработчики с такими ситуациями сталкиваются в своей повседневной деятельности, в разговорах с коллегами, желающими выделится на вашем фоне, и т.д., евангелисты на встречах, семинарах, выступлениях, докладах. У каждого из нас есть свой багаж аргументов и оборонительных тактик, что является естественным. Не всегда выгодно игнорировать человека. Иногда обстоятельства складываются так, что остается только два пути пасовать или побеждать. Для победы, желательно не затягивая дебаты, нужны веские и достаточные аргументы. Какими же эти аргументы должны быть? Возьмем за основу три принципа:

1. Козьма Прутков: «Зри в корень» -> определи - что для оппонента является точкой опоры.
2. Основополагающее правило РРБ: «Лишить противника равновесия максимально эффективно и быстро» -> соответственно выбить почву из под ног, лишив опоры.
3. Геометрический метод: «Доказательство от обратного» -> все действия нужно провернуть с позитивом, не стандартно и оригинально.

Исходя из выше сказанного и того, что ваш оппонент ярко выраженный фанат С++, а так же человек не желающий развиваться дальше достигнутой ступеньки. Возьмем за основу нашей тактики следующую последовательность вопросов:

1. Какая книга и какого автора является для вас библией в вашей профессиональной деятельности как C++ программиста? – естественно автор будет Джеффри Рихтер или вы общаетесь не с программистом.
2. Что из себя представляет Джеффри Рихтер как специалист и кем он является: например по отношению к таким компаниям как Intel, DreamWorks и Microsoft? – нужно понимать, что данным вопросом мы преследуем сразу несколько целей. Во-первых: он подготавливает ситуацию к финальному ходу. Во-вторых: есть возможность заработать бонусные балы себе в глазах свидетелей (в зале например), если человек не владеет информацией глубже литературной деятельности автора. В-третьих: мы получаем добровольное признание оппонента в том, что данный автор является для него авторитетом с абсолютной степенью значимости. «… кто его тронет, он же памятник! …» - Рихтер выступал в роли консультанта для таких компаний как Intel, DreamWorks и Microsoft.
3. И контрольный вопрос: Как вы относитесь к высказыванию Джеффри Рихтера в одной из относительно недавних его книг? А конкретно:

«…Уже несколько лет я использую .NET Framework и должен сказать с уверенностью, что ни за что не вернусь к устаревшим технологиям абстрагирования и способам разработки ПО. И, если меня заставят, я предпочту сменить профессию! Вот как трудно отвыкать от хорошего. Честно говоря, вспоминая, чего стоило создавать приложения с использованием старых технологий, я просто не могу представить, как разработчикам вообще удавалось так долго создавать работающее ПО.»
Джеффри Рихтер, книга «CLR via C# программирование на платформе .NET FRAMEWORK на языке С#» 2-е издание, Введение, страница XIV.

За сим прощаюсь с вами. Вешаю на свой щит, как икону, фото и цитату Рихтера и со словами «изыйдите нечистые» продолжу свой путь.

P.S. Данная статья является результатом стечения обстоятельств и соответствующего настроения )

P.S.S. За статьей последовала переписка с Джеффри Рихтером, которую я привожу с его на то разрешения (Разговорный английский ни когда не был моей сильной стороной):

- Good day Jeffrey. I'm Russian developer and I adore Your books, that has directed me on writing the following article: «Твой путь». This article writing on Russian language. I be very happy if see You comment, if You have possibility.
- Добрый день Джеффри. Я являюсь русским разработчиком и достаточно сильно уважаю ваши книги, что вдохновило меня на написание следующей статьи: «Твой путь». Данная статья написана на русском языке. Я был бы очень счастлив увидеть Ваши комментарии, если Вас это не обременит.
-- Dmitry Timofeev

- I read your post via a translator which didn't do a great job of translating the text but I think I get the general gist idea of what you are trying to say. I have to say that C# is a much more programmer-friendly environment than C++ is. But, I also have to say that C++ can do some things that C# cannot do: more control over your system, better error recovery and, in some cases, better performance. I don't want to give the impression that C# is the solution for every problem. There are some problems that C++ is better suited for: operating systems and database servers come immediately to mind. Personally, I'm in a position where I would not go back to C++ development but there is still a needs for C++ developers as C# can't do everything.
- Я прочитал Ваш пост через переводчик, который не делал большой работы по переводу текста, но думаю, что понял то, что Вы пытаетесь сказать. Должен сказать, что C# является намного более благоприятной для программиста средой, чем C++. Но также должен сказать, что C++ может сделать некоторые вещи лучше, чем C# : возможен больший контроль над системой, лучшее восстановление после ошибок и, в некоторых случаях, лучшая производительность.
Я не хочу создать впечатление, что C# - решение для каждой проблемы. Есть некоторые задачи, для решения которых лучше подходит С++: на ум сразу приходят операционные системы и серверы баз данных. Лично я не хотел бы вернуться назад к разработке на C++, но есть все еще потребность в C++ разработчиках, поскольку C# не может сделать всего.
-- Jeffrey Richter (http://Wintellect.com)

- Thank's from comments. Your words once again emphasize true meaning my article. My opinion comply with You. I have attitude to programing in old school style. Including programming on periods: assembler Z80 (XZ Spectrum), c++, c++ and OpenGL, and in present time C#, particularly pay own attention XNA, it's explain my relation with quoting You words. I ask You permit publication in my blog from You mail comments.
- Спасибо за комментарий. Ваши слова еще раз подтверждают мысль моей статьи. Наши мнения совпадают. Я отношусь к программистам которых относят к старой школе (old school). Прохождение через периоды программирования на: ассемблере Z80 (ZX Spectrum), c++, c++ и OpenGL, и в настоящее время C #, особенно много уделяя собственное внимание XNA, может объяснить мое отношении к цитированию Ваших слов. Я прошу Вашего разрешения публикации Ваших комментариев в моем блоге.
-- Dmitry Timofeev

- Yes you may post what i sent you.
- Да Вы можете опубликовать то, что я послал Вам.
-- Jeffrey Richter (http://Wintellect.com)

четверг, 10 сентября 2009 г.

С#+XNA. В погоне за fps. I

Краткое содержание:

- Введение;
- Часть 1-я. Новичкам;
- Часть 2-я. Та же песня с Microsoft'ом;
- Часть 3-я философская. Охотник или жертва;
- Заключение.


Введение.

Данная статья является попыткой посмотреть в корень проблемы. Проблемы достижения высоких показателей fps с точки зрения построения C# кода не касаясь при этом XNA раздела 3D, относящегося к непосредственным инструкциям видеокарте.


Часть 1-я. Новичкам.

Мои наблюдения основаны на постоянном присутствии в различных форумах и участии в различных проектах. Соль рассматриваемого мною вопроса заключается в использовании Fields и Properties членов классов и структур.
Для полноты ощущений, в моих изысканиях, меня интересовали следующие ссылочные типы:
- class;
- interface;
- object.
Сейчас мы просмотрим на время выполнения кода при различной организации доступа к членам класса.
Пример:


using System;
namespace TestObjectInterface
{
class Program
{
static Unit[] unit;
static object[] unitobj;
static iUnit[] uniti;

static int count;

static void Main(string[] args)
{
count = 10000000;
unit = new Unit[count];
unitobj = new object[count];
uniti = new iUnit[count];
Init();
double tf = TestFields();
double tp = TestProperties();
double tfo = TestFieldsObject();
double tpo = TestPropertiesObject();
double tpi = TestPropertiesInterface();
Console.WriteLine("testFields = "
+ tf.ToString() + " mc, k = 1");
Console.WriteLine("testProperties = "
+ tp.ToString() + " mc, k = "
+ (tp / tf).ToString());
Console.WriteLine("testFieldsObject = "
+ tfo.ToString() + " mc, k = "
+ (tfo / tf).ToString());
Console.WriteLine("testPropertiesObject = "
+ tpo.ToString() + " mc, k = "
+ (tpo / tf).ToString());
Console.WriteLine("testPropertiesInterface = "
+ tpi.ToString() + " mc, k = "
+ (tpi / tf).ToString());
//Console.ReadKey();
}

static void Init()
{
for (int i = 0; i < count; i++)
{
unit[i] = new Unit(i, i * 2);
uniti[i] = (iUnit)unit[i];
unitobj[i] = (object)unit[i];
}
}

static double TestFields()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = unit[i].x + unit[i].y; // 0
n = unit[i].x + unit[i].y; // 1
n = unit[i].x + unit[i].y; // 2
n = unit[i].x + unit[i].y; // 3
n = unit[i].x + unit[i].y; // 4
n = unit[i].x + unit[i].y; // 5
n = unit[i].x + unit[i].y; // 6
n = unit[i].x + unit[i].y; // 7
n = unit[i].x + unit[i].y; // 8
n = unit[i].x + unit[i].y; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestProperties()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = unit[i].X + unit[i].Y; // 0
n = unit[i].X + unit[i].Y; // 1
n = unit[i].X + unit[i].Y; // 2
n = unit[i].X + unit[i].Y; // 3
n = unit[i].X + unit[i].Y; // 4
n = unit[i].X + unit[i].Y; // 5
n = unit[i].X + unit[i].Y; // 6
n = unit[i].X + unit[i].Y; // 7
n = unit[i].X + unit[i].Y; // 8
n = unit[i].X + unit[i].Y; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestFieldsObject()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 0
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 1
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 2
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 3
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 4
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 5
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 6
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 7
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 8
n = ((Unit)unitobj[i]).x + ((Unit)unitobj[i]).x; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestPropertiesObject()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 0
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 1
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 2
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 3
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 4
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 5
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 6
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 7
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 8
n = ((Unit)unitobj[i]).X + ((Unit)unitobj[i]).Y; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestPropertiesInterface()
{
DateTime dtStart = DateTime.Now;
int n;
for (int i = 0; i < count; i++)
{
n = uniti[i].iX + uniti[i].iY; // 0
n = uniti[i].iX + uniti[i].iY; // 1
n = uniti[i].iX + uniti[i].iY; // 2
n = uniti[i].iX + uniti[i].iY; // 3
n = uniti[i].iX + uniti[i].iY; // 4
n = uniti[i].iX + uniti[i].iY; // 5
n = uniti[i].iX + uniti[i].iY; // 6
n = uniti[i].iX + uniti[i].iY; // 7
n = uniti[i].iX + uniti[i].iY; // 8
n = uniti[i].iX + uniti[i].iY; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}
}

interface iUnit
{
int iX {get;}
int iY {get;}
}

public class Unit : iUnit
{
public int x;
public int X { get { return x; } }
int iUnit.iX { get { return x; } }

public int y;
public int Y { get { return y; } }
int iUnit.iY { get { return y; } }

public Unit(int inX, int inY)
{
x = inX;
y = inY;
}
}
}

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


На мой взгляд все достаточно наглядно. Самый скоростной способ это естественно обращение к полю класса на прямую.
А теперь поясню к чему рассказал и продемонстрировал прописную истину. Всем так же известно, что в разработке 3D, в отличие от бизнес, приложений особенно ярко выражена погоня за скоростью. Но тем не менее, специалисты начинающие(!) работать с XNA, уже как правило, достаточно уверенно себя ощущают в области C# разработки. Вот в этом то и заключается основной казус. Который объясняется тем фактом, что вся справочная и обучающая литература пестрит примерами описывающими доступ к полям классов через свойства. В следствии чего ребята начинают автоматически(!) применять данный подход во всех случаях. И там где это действительно необходимо, и где не очень. Да это хороший тон(!). Удобно перехватывать обращения к полю класса, обрабатывать возникающие при этом исключительные ситуации. Но не подходит для применения в 3D(!!!).
Не всегда можно быстро выдать столько информации по данному вопросу очередному собеседнику. Да и сейчас я описал вопрос не за пять минут, но в дальнейшем могу с легкостью ссылаться на себя :).


Часть 2-я. Та же песня с Microsoft'ом.

Не для кого не секрет, что я люблю поковырять рефлектором все, что меня хотя бы мало-мальски интересует. Добавлю то, что порой это приносит больше информации, чем например чтение непосредственно MSDN. Поэтому в довесок к первой части я решил привести один(!) пример из библиотеки XNA.
Рассмотрим XNA 3.1, виндовую библиотеку Microsoft.Xna.Framework.dll,
одноименный namespace, и пусть подопытным будет структура Vector3.
Вот часть кода данной структуры, в рамках которой мы сейчас и пообщаемся:


public struct Vector3
{
public float X;
public float Y;
public float Z;
...
private static Vector3 _one;
public static Vector3 One
{
get
{
return _one;
}
}
...
public Vector3(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
...
static Vector3()
{
...
_one = new Vector3(1f, 1f, 1f);
...
}
}

К полям X, Y и Z вопросов нет. Но обращаю ваше внимание на свойство One. По логике One возвращает единичный вектор постоянного значения. А вот тут по подробнее. Разум цепляется за формулировку "постоянное значение". Читаем MSDN и выясняем, что переменная или поле с постоянным значением может быть объявлена при помощи двух ключевых слов модификаторов доступа, которыми являются const и readonly. На всякий случай поясню формулировкой, переменная или поле постоянного значения бывает двух видов:
- const -> применяется при объявления полей и локальных переменных, постоянное значение присваивается на этапе компиляции, исключительно(!) при объявлении;
- readonly -> применяется при объявления полей, значение может присваивается как при объявлении, так и в конструкторе данного класса или структуры.
Но речь идет о поле постоянного значения которое не возможно рассчитать при компиляции, что накладывает ряд ограничений и в соответствии с рассматриваемым нами случаем, расширим формулировки:
- Модификатор static не допускается в объявлении константы;
- значение константы должно быть полностью вычислено во время компиляции;
- Единственными возможными значениями для констант ссылочных типов являются string и null;
Ясное дело на static ставим крест, он однозначно не подходит. А вот readonly наш размерчик! И как нельзя лучше вписывается в нашу ситуацию. Потому, заранее уже зная куда нас это приведет, предлагаю по тестировать и сравнить время чтения значения из свойства стандартной структуры с чтением значения из иначе построенной структуры.
Смотрим пример:


using System;
using Microsoft.Xna.Framework;
namespace TestFields
{
class Program
{

static int count;

static void Main(string[] args)
{
count = 10000000;
Console.WriteLine("testStandartVector3 = " +
TestStandartVector3().ToString() +
" mc");
Console.WriteLine("testMyVector3 = " +
TestMyVector3().ToString() +
" mc");
}

static double TestStandartVector3()
{
DateTime dtStart = DateTime.Now;
Vector3 vec;
for (int i = 0; i < count; i++)
{
vec = Vector3.One; // 0
vec = Vector3.One; // 1
vec = Vector3.One; // 2
vec = Vector3.One; // 3
vec = Vector3.One; // 4
vec = Vector3.One; // 5
vec = Vector3.One; // 6
vec = Vector3.One; // 7
vec = Vector3.One; // 8
vec = Vector3.One; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}

static double TestMyVector3()
{
DateTime dtStart = DateTime.Now;
gVector3 vec;
for (int i = 0; i < count; i++)
{
vec = gVector3.One; // 0
vec = gVector3.One; // 1
vec = gVector3.One; // 2
vec = gVector3.One; // 3
vec = gVector3.One; // 4
vec = gVector3.One; // 5
vec = gVector3.One; // 6
vec = gVector3.One; // 7
vec = gVector3.One; // 8
vec = gVector3.One; // 9
}
DateTime dtEnd = DateTime.Now;
return (dtEnd - dtStart).TotalMilliseconds;
}
}

public struct gVector3
{
public float X;
public float Y;
public float Z;

public static readonly gVector3 One;

public gVector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}

static gVector3()
{
One = new gVector3(1, 1, 1);
}
}
}

Получаем вот такой вот результат:


Внимание вопрос(!): какая была необходимость реализовывать "постоянное значение" через свойство возвращающее значение приватного поля, если в сравнительном отношении скорости, с реализацией через модификатор доступа readonly, составляет разницу практически в(!) два раза? Мне не понятно, а вам? Буду очень признателен тому человеку, который вежливо мне объяснит.
Товарищам, которые планируют достаточно часто применять постоянные вектора:
- Zero;
- One;
- UnitX;
- UnitY;
- UnitZ;
- Up;
- Down;
- Right;
- Left;
- Forward;
- Backward.
Рекомендую не использовать стандартные, а реализовать самостоятельно, как в выше приведенном примере.
Мысль которую я хотел донести, звучит следующим образом:
- изучайте все средства, которые собираетесь применять в своих проектах;
- в результате изучения берите только лучшее и делайте наконец то сказку былью;
- не знание законов не освобождает от ответственности. Это про специалистов, которые по своей беспечности, не разбираясь, применяют все подряд, а в последствии сетуют на Microsoft. Хотя это говорит с плохой стороны не(!) о Microsoft.
- допуская то, что не смотря на "много букв", у читателей данной статьи мнения разойдутся. Я добавил третью часть. Читать рекомендую всем, только одним принять к сведению, а иным просто улыбнуться.


Часть 3-я философская. Охотник или жертва?

Жертвам своей не компетентности, которые любят плохо отзываться о Microsoft, посвящается.
Что, из себя, представляет понятие мода? Мода - в широком смысле этого слова, определяет стремление, того или иного человека, быть похожим на основное большинство. Хм, так то оно так, когда в меру, без фанатизма и крайностей. А так же не идет в разрез с законами эволюции, которые не зависимо от нас хранят приоритетное право "сильных" и "особенно удачных" особей выживать. К примеру обратная крайность это не формальные движения в нашем обществе.
Применяем выше описанное отступление к нашей основной теме. И так у нас есть три основных типа людей. Систематизируем их и результат оформим в виде структуры:
- ярко выражено не(!) довольные Microsoft'ом -> модные "жертвы". Да, уже более десяти лет считается модно и наверное круто так себя вести;
- фанаты Microsoft'а -> назовем их "панки", так веселее смотрится не формальное определение. Обычно эти люди либо являются специалистами, но узкого профиля, либо совсем не являются таковыми. Если кого то обидел, сообщите мне. Я подберу более мягкое определение.
- специалисты -> "охотники". Ни кому не покланяются, ни кого не ругают. Им некогда отвлекаться на разную чушь. Эти люди берут от жизни только лучшее, прекрасно понимая при этом как и где это лучшее применять, и как и где не стоит. Они же становятся MCP, MVP, "черными поясами" Intel, … , и другими почетными товарищами.


Заключение.

Ну вот и все. Всем перца насыпал. Развеялся. Можно дальше "охотиться" ... оговорился :) ... работать и точить свое мастерство.