Задать вопрос
  • Property и Fields?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Да, при создании автосвойства создаётся скрытая переменная - данные должны где-то храниться.

    Эта переменная создаётся с хитрым именем (с запрещёнными в c# символами в имени переменной). Можно увидеть это таким способом:
    class TestClass
    {
        private int _a;
        public int A
        {
            get { return _a; }
            set { _a = value; }
        }
    
        public int B { get; set; }
    }
    
    var testClass = typeof(TestClass);
    var fields = testClass.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
    foreach (var fieldInfo in fields)
    {
        Console.WriteLine(fieldInfo.Name);
    }
    // результат:
    // _a
    // <B>k__BackingField
    Ответ написан
    4 комментария
  • Как сработает переопределение?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    В классе A метод Print отмечен виртуальным, то есть, его можно переопределить. Но в наследнике B нет переопределения этого метода, а создаётся новый виртуальный метод с тем же именем - ключевое слово new указывает, что создаётся новый метод, не связанный с методом A.Print.
    В классе C метод Print переопределяет метод родителя, то есть, метод B.Print, но не переопределяет метод A.Print.
    Так как у переменной указан тип A, то вызов ac.Print() вызовется метод - единственный из реализаций метода A.Print.

    Вот смотрите, чтобы было понятнее:
    class A { public virtual void Print() { Console.WriteLine("A"); } }
    class B : A { public override void Print() { Console.WriteLine("B"); } }
    class C : B { public new virtual void Print() { Console.WriteLine("C"); } }
    class D : C { public override void Print() { Console.WriteLine("D"); } }
    
    A ad = new D();
    ad.Print(); // покажет B
    Ответ написан
    3 комментария
  • Как строку форматировать в функцию (C#)?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    lam0x86 сказал всё правильно, я распишу про сложный путь.
    Нужно разделить строку на части, состоящие из чисел и операций.
    В простом случае (когда два числа и одна операция) это можно сделать однократным поиском str.IndexOfAny(new []{'+', '-', '*', '/'}), который вернёт индекс символа операции.
    В общем случае (когда несколько операций в строке) нужно искать циклом и добавлять найденные части в список, и потом обрабатывать его другим циклом. Более правильно, но более сложно - учитывать приоритеты операций (сначала умножение, потом сложение).
    Если в строке возможны скобки, то лучше части заполнять не в список, а в специальный класс, который будет разбирать полученные части в дерево вычислений.
    Ответ написан
    6 комментариев
  • Какие различия между Parse и Convert.To?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Чтобы разобраться, в чём разница, то смотрите источник - исходный код вот здесь (в строке 887).
    В общем, Parse берёт строку и возвращает число (возможно, будет исключение). Convert.ToInt16 имеет много вариантов, в зависимости от типа аргумента. Конкретно Convert.ToInt16(string) вызывает Int16.Parse(value, CultureInfo.CurrentCulture), то есть, абсолютно нет разницы с Parse (в том числе, возникнет исключение, если строка не является числом).
    Ответ написан
  • Как обработать событие из DataTemplate нажатия радиокнопок?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    ListBox (как и другой наследник ItemsControl) имеет список элементов (удобнее всего его указывать через ItemsSource). Каждый элемент списка строится отдельным пунктом. У каждого пункта автоматически устанавливается DataContext - тот элемент исходного списка, который строится. В DataTemplate хорошо и правильно указывать нужный тип, который будет у элемента в датаконтексте, тогда появится подсказка у {Binding} внутри шаблона.

    public class Item : BaseViewModel
    {
        private string _name;
        public string Name 
        { 
            get => _name;
            set
            {
                _name = value;
                RaisePropertyChanged();
            }
        }
        
        private int _itemMode;
        public int ItemMode 
        { 
            get => _itemMode;
            set
            {
                _itemMode = value;
                RaisePropertyChanged();
            }
        }
    }
    
    public class ItemsList
    {
        public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>
        {
            new Item { Name = "Один", ItemMode = 1 },
            new Item { Name = "Два", ItemMode = 2 },
            new Item { Name = "Три", ItemMode = 3 },
        };
    }

    <Grid>
        <Grid.Resources>
            <converters:IntToBoolConverter x:Key="IntToBoolConverter"/>
            <DataTemplate DataType="{x:Type viewModels:Item}">
                <Border x:Name="Data">
                    <Grid x:Name="ContentBase" Margin="1,0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Label Name="DisplayText" Grid.Column="0" Content="{Binding Name}"/>
                        <RadioButton Name="_1" Grid.Column="1" Content="1" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=1}"/>
                        <RadioButton Name="_2" Grid.Column="2" Content="2" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=2}"/>
                        <RadioButton Name="_3" Grid.Column="3" Content="3" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=3}"/>
                    </Grid>
                </Border>
            </DataTemplate>
        </Grid.Resources>        
        <Grid.DataContext>
            <viewModels:ItemsList/>
        </Grid.DataContext>
        <ListBox ItemsSource="{Binding Items}"/>
    </Grid>

    Код класса BaseViewModel
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    Код конвертера
    public class IntToBooleanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value != null 
                && parameter != null 
                && value.ToString() == parameter.ToString();
        }
     
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || parameter == null) return Binding.DoNothing;
    
            int result;
            if ((bool)value && int.TryParse(parameter.ToString(), out result))
            {
                return result;
            }
    
            return Binding.DoNothing;
        }
    }

    Ответ написан
    8 комментариев
  • Подмена реализации интерфейса без перекомпиляции проекта. Как реализовать?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    //m = new Message();
    m = MessageFactory.GetMessage();

    А вот в MessageFactory уже искать тот метод, который создаст нужный объект (например, как указал freeExec).

    Как вариант - использовать IoC-контейнер:
    1) в главной сборке создать контейнер.
    2) найти нужную сборку и передать в неё этот контейнер
    3) в той сборке забиндить нужный класс на этот интерфейс в переданном контейнере.

    Тот же приём можно использовать, когда в решении есть два проекта, второй проект использует первый проект (первый добавлен в зависимостях), а вот первому нельзя добавить второй проект в зависимостях. Решение - контейнер в первом проекте, а классы биндятся во втором. Тогда пункт 2 не нужен.
    Ответ написан
    Комментировать
  • Скрыть область видимости переменной?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Переменную можно поместить в блок, тогда переменная будет ограничена только этим блоком:
    int Test()
    {
        int result = 0;
    
        {
            int i = 10;
            for (int j = 0; j < 10; j++)
            {
                result += i;
            }
        }
        {
            string i = "xyz";
            Console.WriteLine(i);
        }
        return result;
    }
    Ответ написан
    1 комментарий
  • Как избежать проблем с Execution Order?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Я в Awake использую инициализацию самого компонента, не используя связи с другими компонентами. А в Start все компоненты уже инициализированы, их можно использовать.

    Компонент A в Start получает ссылку на компонент B. Не факт, что B уже вызвал Start и не имеет нужные ссылки. Тут два варианта
    - B тоже должен иметь ссылку на A. Тогда A может передать себя в B (до вызова B.Start).
    - B должен иметь ссылку на компонент C. Тут могут быть проблемы.

    Чтобы такие связи были независимы от порядка, то каждый компонент в Awake должен поместить себя (зарегистрироваться) в какой-то контейнер, через который в Start в любом компоненте можно получить нужную ссылку.

    Контейнером может быть
    - статическое поле класса (если компонент планируется только один, то получается типа полу-синглтон - поле устанавливается в Awake, а потом используется отовсюду во время Start и после)
    - специальный список в родителе (если компонент не одиночка)
    - IoC-контейнер
    Ответ написан
    4 комментария
  • Можно ли объявлять делегат в одном классе (методы там же), а создавать переменную, присваивать адрес метода, вызывать в другом классе?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    А кто вам мешает так делать?.. Делегат можно объявить вообще не в классе, а просто в неймспейсе. Ведь делегат - это просто сигнатура метода (количество и типы аргументов метода).
    Ответ написан
    Комментировать
  • Как реализовать Таблицу Менделеева?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Предполагаю, что делается WPF-проект.

    1. Сделать класс-вьюмодель элемента с нужными свойствами для описания элемента.

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

    3. Создание таблицы элементов. Также нужна возможность сделать пустые ячейки (например, между водородом и гелием). Список ячеек показывать через ItemsControl с нужным вариантом панели: WrapPanel или Grid. Для показа разных типов ячеек (элемент, пустая ячейка, ячейка с текстом) можно использовать различные шаблоны (указав DataTemplate c типом или через ItemTemplateSelector).

    а) (самый простой вариант) панель WrapPanel с нужной шириной таблицы и с нужной шириной/высотой элемента. Неиспользуемые ячейки просто показывать пустоту (или что-нибудь показать внутри этой ячейки). Каждая пустая ячейка должна быть в списке ячеек отдельным элементом. WrapPanel сама покажет все переданные ячейки в виде таблицы.

    б) (возможность написать что-то в строке между H и He) панель WrapPanel с нужной шириной. Во вьюмодели сделать свойство int Span для тех ячеек, которые будут заниматься несколько ячеек. По умолчанию значение 1 (одна ячейка). В контрол привязывать ширину контрола ячейки с свойством Span (с помощью конвертера, я бы назвал MultiplyConverter)

    в) (более сложно, но больше возможностей - объединение ячеек по горизонтали и вертикали) панель Grid с 8 колонками и сколько-то там строками (или какой вариант таблицы вы хотите использовать). Во вьюмодели сделать свойства Row и Col, а также RowSpan и ColSpan, привязывать их в одноимённые свойства в контроле (attached properties Grid.Row и т.п.).
    Ответ написан
    Комментировать
  • Как анимировать image в wpf при нажатии на другую кнопку?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    По кнопке изменить свойство во вьюмодели (возможно, булевое свойство). Триггер по изменению этого свойства вызовет анимацию.
    Ответ написан
    1 комментарий
  • Как включить отображение XAML Designer Window в Visual Studio 2017?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Судя по картинке, у вас студия разделила xaml и xaml.cs, а не поняла их как общую сущность. И теперь xaml показывает как простой текстовый файл. Возможно, студия не поняла, какой тип шаблона этого проекта (может, проект не созданный вновь, а подключенный существующий?).

    Создайте в этом же решении новый проект с типом WpfApplication и попробуйте дизайнер в нём. Сравните файлы csproj между вашим дефектным проектом и вновь созданным проектом.
    Ответ написан
    Комментировать
  • Кодер -> Программист -> Архитектор?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    У меня есть личное описание уровней программиста, похожее на указанную градацию:
    - Джуниор пишет код так, как понял задачу заказчика
    - Миддл пишет код так, чтобы хорошо и правильно решить задачу заказчика
    - Синьор пишет так, чтобы сделать будущую задачу заказчика (чтобы код проекта был готов к будущим задачам, без переписывания всего проекта с нуля)

    Синьор имеет опыт и понимает, что в дальнейшем понадобится в проекте (если нужно - спросит, если нет, то догадается). Именно синьор может быть архитектором, который занимается инфраструктурой проекта, чтобы другие программисты переиспользовали уже готовые части проекта.
    Миддл тоже имеет развитые навыки проектирования архитектуры, но часто работает со средними и большими фичами (частями), а не с системными вещами.
    Джуниор делает маленькие задачки, его навыки архитектуры относятся к одному или нескольких связанных классов для текущей задачи. Хороший менеджер подбрасывает задачи чуть сложнее, чтобы дать возможность попрактиковаться в архитектуре.
    Ответ написан
    Комментировать
  • Для чего нужны спецификаторы доступа в C#/Java и др. подобных языках?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    class KvadrUr
    {
        public double a, b, c, D, x1, x2;
        public void CalcD()
        {
            D = b*b - 4*a*c;
        }
        public void Calc()
        {
            double d  = Math.Sqrt(D);
            x1 = (-b + d)/(2*a);
            x2 = (-b - d)/(2*a);
        }
    }
    KvadrUr ur = new KvadrUr();
    ur.a = 1;
    ur.b = 2;
    ur.c = -3;
    ur.CalcD();
    ur.Calc();
    Console.WriteLine(ur.x1 + " " + ur.x2); // правильный ответ
    ur.c = -4;
    Console.WriteLine(ur.x1 + " " + ur.x2); // неправильный ответ - теперь дискриминант неверный!

    Если сделать переменную D приватной (и вызывать CalcD внутри Calc), то это эта ошибка возникнуть не может.
    То есть, ограничение доступа к членам класса помогает уменьшать вероятность ошибки.
    Ответ написан
    Комментировать
  • Алгоритм равномерного распределения заявок?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    foreach (var newOrder in newOrders)
    {
        // ищем минимальное количество заявок среди всех менеджеров
        var minOrdersCount = managers.Min(m => m.Orders.Count);
        // ищем менеджера с найденным количеством (если несколько - берём первого)
        var manager = managers.First(m => m.Orders.Count == minOrdersCount);
        // даём заявку менее занятому менеджеру
        manager.Orders.Add(newOrder);
    }

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

    Вот алгоритм посложнее, в нём поисков по всем менеджерам происходит меньше (особенно, когда большое количество новых заявок):
    class Manager
    {
        public string Name { get; private set; }
        public List<int> Orders { get; private set; }
    
        public Manager(string name, int ordersCount = 0)
        {
            Name = name;
            Orders = ordersCount > 0
                ? Enumerable.Range(1, ordersCount).ToList()
                : new List<int>();
        }
    }
    
    static void Main(string[] args)
    {
        var managers = new Manager[]
        {
            new Manager("Лена", 1), 
            new Manager("Оля", 10), 
            new Manager("Иван", 35), 
            new Manager("Сергей", 75), 
        };
        var newOrders = Enumerable.Range(1, 35).ToList();
        var newOrdersCount = newOrders.Count;
        var ordersAssigned = 0;
    
        while (ordersAssigned < newOrdersCount)
        {
            var ordersCounts = managers.Select(m => m.Orders.Count).OrderBy(count => count).Distinct().ToArray();
            var addingOrdersCount = ordersCounts.Length > 1 ? ordersCounts[1] - ordersCounts[0] : ordersCounts.First();
            var managersWithMinOrders = managers.Where(m => m.Orders.Count == ordersCounts[0]).ToArray();
            // нашли менеджеров с минимальным количеством заявок
            if (managersWithMinOrders.Length * addingOrdersCount < newOrdersCount)
            { // заполняем самых незанятых менеджеров до того же уровня занятости
              // т.е. добавляем Лене (10 - 1) = 9 заявок
                foreach (var manager in managersWithMinOrders)
                {
                    for (int i = 0; i < addingOrdersCount; i++)
                    {
                        manager.Orders.Add(newOrders[ordersAssigned]);
                        ordersAssigned++;
                    }
                }
            }
            else
            {
                // незанятых менеджеров нет, заполняем оставшиеся заявки по менеджерам по очереди
                while (ordersAssigned < newOrdersCount)
                {
                    var managerIndex = ordersAssigned % managersWithMinOrders.Length;
                    managersWithMinOrders[managerIndex].Orders.Add(newOrders[ordersAssigned]);
                    ordersAssigned++;
                }
            }
        }
    
        foreach (var manager in managers)
        {
            Console.WriteLine("{0}: {1} заявок", manager.Name, manager.Orders.Count);
        }
        Console.ReadKey();
    }
    Ответ написан
    1 комментарий
  • Что это за конструкция public T GetService() where T: class { throw new NotImplementedException(); } в C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Это объявление обобщённого метода, в котором тело не определено.
    В этом коде три разных концепции:
    1) это объявление метода GetService без аргументов и с возвращаемым типом T
    2) В качестве возвращаемого типа стоит T - это обобщённый (generic) тип, реальный тип будет указан при вызове метода. Слово where указывает ограничение - реальным типом должен быть класс.
    Например, можно указать MyService x = GetSevice<MyService>(); или Person x = GetSevice<Person>(); или IWeapon x = GetSevice<IWeapon>();.
    3) в качестве тела метода стоит throw new NotImplementedException(); - возникнет исключение, указывающее, что тело не определено. Так делается, если нужно показать, что этот код вызывать не нужно, либо если тело будет написано позже, а сейчас при вызове будет исключение.
    Ответ написан
    2 комментария
  • Как полностью очистить массив?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Возможно, лучше взять список List<int> вместо массива:
    public List<int> checkAddDel;
    
    checkAddDel = new List<int>(Word.Length); // предварительно указывать размер не обязательно, 
                                              // но если известно заранее, то лучше указать
    
    Console.WriteLine(checkAddDel.Count); // 0
    for (var i = 0; i < Word.Length; i++)
    {
        checkAddDel.Add((i + 1) * 10);
    }
    Console.WriteLine(checkAddDel.Count); // размер теперь стал равным Word.Length
    
    // используем список
    DoWork(checkAddDel);
    
    checkAddDel.Clear(); // теперь стал пустым
    Console.WriteLine(checkAddDel.Count); // 0
    Ответ написан
    Комментировать
  • Как сделать выпуск софта?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Ответ написан
    Комментировать
  • Как создать систему заданий в игре?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Хороший ответ Yegor, добавлю ещё другую таблицу: условия появления заданий - какая задача появляется после какой. Туда же можно добавить другие условия: время, уровень, наличие предметов или наоборот отсутствие (исчез важный предмет - добавить необязательное задание для получения этого предмета заново).
    Ответ написан
    Комментировать
  • Как сделать так, чтобы каждый if выполнался одновременно?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Проверяйте не отдельные клавиши, а сочетания:
    var isLeftPressed = Input.GetButton("LeftArrow");
    var isUpPressed = Input.GetButton("UpArrow");
    var isRightPressed = Input.GetButton("RightArrow");
    var isDownPressed = Input.GetButton("DownArrow");
    if (isUpPressed && isDownPressed && Time.time > timeToFire)
    {
        timeToFire = Time.time + 1 / fireRate;
        Instantiate(fireSphere, firePoint.position, firePoint.rotation);
    }
    Ответ написан
    Комментировать