Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Указатель на функцию

1.8K
10 июля 2009 года
rSolanov
106 / / 04.05.2005
Здравствуйте!
Вопрос собственно по указателям.
Поскольку я в C# недавно, то с делегатами пока не очень и потому прошу Вашей помощи.
Есть код, который вызывает функцию FRun (третья строчка снизу):
Код:
private void backgroundWorker_DoWork(object sender,
            DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if (worker.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                int n = (int)e.Argument;
                int percentComplete =
                    (int)((float)n / (float)numberToCompute * 100);
                if (percentComplete > highestPercentageReached)
                {
                    highestPercentageReached = percentComplete;
                    worker.ReportProgress(percentComplete);
                }
                e.Result = FRun(n, worker, e);
            };
        }

Тело функции FRun может быть разным и описывается в другом модуле (это нужно для работы различных алгоритмов в отдельном потоке)
Представленный метод является частью класса формы
 
Код:
public partial class F_Wait : Form

Я хочу вызывать это окно из любого модуля с передачей указателя (делегата) на функцию в конструкторе:
 
Код:
F_Wait Wait = new F_Wait(Указатель на функцию);

Тем самым FRun всегда отработает тело той функции, которую мы передали по указателю.
Теперь у меня пошли затруднения.
Как я понимаю, сначала необходимо описать указатель на функцию как тип.
Правильно ли это не знаю, но думаю что так:
 
Код:
public delegate long FHandler(int n, BackgroundWorker worker, DoWorkEventArgs e);

А теперь необходимо собственно написать эту функцию и передать ее в конструктор формы.
Для простоты функция будет просто выдавать число (например 5).
Конструктор формы:
 
Код:
private FHandler FRun;
        public F_Wait(FHandler F)
        {
            InitializeComponent();
            InitializeBackgoundWorker();
            FRun += new FHandler(F);
        }

Правильно ли это? И как написать саму функцию, в которая будет тут отрабатывать и передать ее в конструктор?
5
10 июля 2009 года
hardcase
4.5K / / 09.08.2005
Посмотрите мой вариант - типичное окно с прогрессбаром и кнопкой прерывания работы.
Класс формы FormWithProgressBar. На форме progressBar1 (для показа прогресса), button1 (кнопка "Отмена"), backgroundWorker1 (инкапсулятор потока). Компонентам назначены соответствующие события.
Свойства backgroundWorker1.WorkerReportsProgress и WorkerSupportsCancellation установлены в true.
Свойство DialogResult у button1 установлен в DialogResult.Cancel.
Код:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication9 {
   
    public partial class FormWithProgressBar : Form {
       
        public FormWithProgressBar(WorkerHandler worker_handler) {
            if (ReferenceEquals(null, worker_handler))
                throw new ArgumentNullException("worker_handler");

            InitializeComponent();
            this.worker_handler = worker_handler;
        }

        private WorkerHandler worker_handler;

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
            progressBar1.Value = e.ProgressPercentage;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            DialogResult = e.Cancelled ? DialogResult.Cancel : DialogResult.OK;
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
            e.Cancel = !worker_handler(backgroundWorker1);
        }

        private void FormWithProgressBar_Load(object sender, EventArgs e) {
            backgroundWorker1.RunWorkerAsync();
        }

        public static bool RunBackgroundWork(WorkerHandler handler) {
            using (FormWithProgressBar form = new FormWithProgressBar(handler)) {
                return form.ShowDialog() == DialogResult.OK;
            }
        }

        private void FormWithProgressBar_FormClosing(object sender, FormClosingEventArgs e) {
            if (backgroundWorker1.IsBusy) {
                backgroundWorker1.CancelAsync();
                e.Cancel = true;
            }
        }

        private void button1_Click(object sender, EventArgs e) {
            backgroundWorker1.CancelAsync();
        }

    }

    public delegate bool WorkerHandler(BackgroundWorker worker);

}
Форма вызывается как диалоговое окно. В случае успеха WorkerHandler она возвращает DialogResult.OK. Предполагается, то WorkerHander в процессе своей работы периодически обращается к свойству CancellationPending объекта worker.


Вот пример использования:
Код:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication9 {
    public partial class MainForm : Form {
        public MainForm() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {
            Text = "runnning task1";
            bool result = FormWithProgressBar.RunBackgroundWork(delegate(BackgroundWorker woker) {
                for (int i = 0; i < 100; ++i) {
                    Thread.Sleep(100);

                    woker.ReportProgress(i);

                    if ( (i % 10 == 0) && woker.CancellationPending) {
                        return false;
                    }
                }
                return true;
            });
            Text = result ? "Succeded" : "Canceled";
        }
    }
}
1.8K
13 июля 2009 года
rSolanov
106 / / 04.05.2005
Пока я сам разбирался и как всегда бывает, все оказалось не сложно:
В дополнении уже вышеописанному необходимо написать тело функции, которое точно повторяет вид делегата, например:
Объявление делегата:

 
Код:
public delegate long FHandler(int n, BackgroundWorker worker, DoWorkEventArgs e);

Код функции:
 
Код:
public long CreateReport(int n, BackgroundWorker worker, DoWorkEventArgs e)
{
    long r;
    //Код
    return (r)
}

Описание конструктора формы и некоторых полей:
 
Код:
private int FNumberToCompute = 0; //Кол-во шагов
private int highestPercentageReached = 0;
private FHandler FRun;
public F_Wait(FHandler F, int NumberToCompute)
{
     InitializeComponent();
     InitializeBackgoundWorker();
     FRun = F;
     FNumberToCompute = NumberToCompute;
}

Теперь вызываем вызываем конструктор класса-окна и передаем туда указатель на функцию:
 
Код:
F_Wait Wait = new F_Wait(new FHandler(CreateReport), 10);
Wait.ShowDialog(this);

Единственно, что не заработало (конечно это можно оформить как отдельную тему)
то тот момент, что при выполнении алгоритма переданной по указателю функции, эта форма "подвисает" и расположенная на ней кнопка "Отмена" недоступна для нажатия.
Вот обработчик на нажатие кнопки "Отмена":
 
Код:
private void B_Click(object sender, EventArgs e)
        {
            backgroundWorker.CancelAsync();
           
        }

Вот для тестирования написал эту функцию:
Код:
public long CreateReport(int n, BackgroundWorker worker, DoWorkEventArgs e)
        {
            int i = 1;
           
            int j = 0;
               
            for (i = 1; i <= 100000; i++)
            {
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                }
                else
                {

                    if (i == 10000) j = 10;
                    if (i == 20000) j = 20;
                    if (i == 30000) j = 30;
                    if (i == 40000) j = 40;
                    if (i == 50000) j = 50;
                    if (i == 60000) j = 60;
                    if (i == 70000) j = 70;
                    if (i == 80000) j = 80;
                    if (i == 90000) j = 90;
                    if (i == 100000) j = 100;
                    worker.ReportProgress(j);
                }
            }
            return (i);
         }

Что интересно, прогрессбар работает как надо, несмотря на то, что окно вцелом находится в "подвисшем состоянии", хотя работа этой функции вынесена в отдельный поток.
Я смотрю Вы помимо инкапсулятора потока явно используете класс-поток и вызываете
 
Код:
Thread.Sleep(100);

Может все дело в этой строчке и поэтому Ваше окно не подвисает?
Каким образом можно сделать так, чтобы на кнопку "Отмена", расположенную в этом окне, можно было нажать?
5
13 июля 2009 года
hardcase
4.5K / / 09.08.2005
Попробуйте поиграть с моим кодом. Логика его работы вам совершенно подходит. Для проверки состояния отмены задачи передается объект worker, остальные параметры можно легко передать, например, через замыкание.
Цитата: rSolanov

то тот момент, что при выполнении алгоритма переданной по указателю функции, эта форма "подвисает" и расположенная на ней кнопка "Отмена" недоступна для нажатия.


Вы слишком часто обращаетесь к worker-у, а именно на каждой итерации: сто тысяч раз. Обращение к свойству CancellationPending и методу ReportProgress приводят к синхронизации с потоком интерфейса, что приводит к "замораживанию" формы - поток интерфейса не обрабатывает сообщения из очереди ввиду того что происходят (в вашем случае) некоторые сервисные действия в выделенном потоке BackgroundWorker-а.

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

Код:
private void button1_Click(object sender, EventArgs e) {
    Text = "runnning task1";
    bool result = FormWithProgressBar.RunBackgroundWork(MakeComputationWorker(100000000));
    Text = result ? "Succeded" : "Canceled";
}

private static WorkerHandler MakeComputationWorker(int n) {
    return delegate(BackgroundWorker worker) {
        int percent = n / 100;

        for (int i = 1; i <= n; ++i) {
            if (i % percent == 0) {
                if (worker.CancellationPending)
                    return false; // сообщаем о прерывании
               
                worker.ReportProgress(i / percent);
            }                                        
        }
        return true; // все OK - задача завершена
    };
}

Кстати, я тут нашел местечко, череватое багами - метод backgroundWorker1_ProgressChanged, вот подправленная версия:
 
Код:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
    progressBar1.Value = Math.Min(progressBar1.Maximum, Math.Max(progressBar1.Minimum, e.ProgressPercentage));
}
1.8K
13 июля 2009 года
rSolanov
106 / / 04.05.2005
Честно говоря я не понял такую конструкцию:
 
Код:
private static WorkerHandler MakeComputationWorker(int n) {
    return delegate(BackgroundWorker worker) {

Не понимаю зачем возвращать WorkerHandler
Не понимаю как тут работает return delegate(BackgroundWorker worker)
1.8K
13 июля 2009 года
rSolanov
106 / / 04.05.2005
Если написать так, как Вы сказали:
Код:
public bool CreateReport(int n, BackgroundWorker worker, DoWorkEventArgs e)
        {
            int percent = n / 100;

            for (int i = 1; i <= n; ++i)
            {
                if (i % percent == 0)
                {
                    if (worker.CancellationPending)
                        return false; // сообщаем о прерывании

                    worker.ReportProgress(i / percent);
                }
            }
            return true;
         }

И затем вызов:
 
Код:
F_Wait Wait = new F_Wait(new WorkerHandler(CreateReport), 100000000);
                            Wait.ShowDialog(this);

То все работает как часы. Огромное Вам спасибо за помощь!
Как я понял, главное не делать частый вызов worker'а, правильно?
А почему если часто вызывать, то как Вы сказали происходит синхронизация потоков, но почему при этом окно не обрабатывает сообщения?
5
13 июля 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: rSolanov
А почему если часто вызывать, то как Вы сказали происходит синхронизация потоков, но почему при этом окно не обрабатывает сообщения?

Поток интерфейса занимается выборкой сообщений ОС из очереди. Во время синхронизированного вызова этот поток фактически блокируется - ожидается выход другого потока (рабочий поток BackgroundWorker) из синхронизированного контекста. Если очень часто производить подобную синхронизацию, эффект будет равносилен выполнению длительного цикла в обработчике щелчка мышки на кнопке: сообщения никуда не пропадут, они будут обработаны (и очень быстро!) как только поток интерфейса перестанут прерывать.

Цитата: rSolanov
Честно говоря я не понял такую конструкцию:
 
Код:
private static WorkerHandler MakeComputationWorker(int n) {
    return delegate(BackgroundWorker worker) {
Не понимаю зачем возвращать WorkerHandler
Не понимаю как тут работает return delegate(BackgroundWorker worker)

Конструкция вида delegate(BackgroundWorker worker) { ... }; называется анонимной функцией - лямбдой. Тип возвращаемого ею значения выводится из использования предложения return в теле (в примере: return true; т.е. System.Boolean), и он эквивалентен типу-делегатов WorkerHandler.
Вообще, показанная конструкция очень похожа на механизм каррирования в функциональных языках.

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

1.8K
13 июля 2009 года
rSolanov
106 / / 04.05.2005
Цитата: hardcase

Конструкция вида delegate(BackgroundWorker worker) { ... }; называется анонимной функцией - лямбдой. Тип возвращаемого ею значения выводится из использования предложения return в теле (в примере: return true; т.е. System.Boolean), и он эквивалентен типу-делегатов WorkerHandler.
Вообще, показанная конструкция очень похожа на механизм каррирования в функциональных языках.

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


Я с этим еще не встречался. Это меня очень заинтересовало, обязательно об этом почитаю.
Вижу Вы очень начитаны о различных приемах и технологиях программирования.
Можно Вам тогда вопрос не по теме: в Delphi например, можно создавать функции (процедуры) внутри других функций (процедур). Соответственно время жизни и область видимости таких функций - это функция, в которую она заключена. А можно ли подобные конструкции создавать в C#?

5
14 июля 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: rSolanov
в Delphi например, можно создавать функции (процедуры) внутри других функций (процедур). Соответственно время жизни и область видимости таких функций - это функция, в которую она заключена. А можно ли подобные конструкции создавать в C#?


Можно. И в некоторых случаях даже нужно. Однако, компилятор C# достаточно туповат и не умеет автоматически создавать определения типов-делегатов для анонимных функций, каждую анонимную функцию нужно приводить (явно или неявно) к тому или иному типу делегата.

Сама платформа не предоставляет готовых типов делегатов, но их можно легко определить самому. У меня есть специальный набор делегатов для определения вложенных функций и процедур (функций, возвращающих void).

Код:
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication24 {

    using Hardcase.Common.Utils;

    class Program {

        static void Main(string[] args) {
            Proc<int, string> PrintIntString = delegate(int x, string s) { //неявное приведение типа делегата
                Console.WriteLine("Int: {0}, String: {1}", x, s);
            };

            Func<int, int, int> Sum = delegate(int a, int b) {
                return a + b;
            };


            PrintIntString(Sum(10, 23), "Hello world!");

            Console.ReadLine();
        }
    }

}


namespace Hardcase.Common.Utils {

    public delegate void Proc();

    public delegate void Proc<TParam1>(TParam1 param1);

    public delegate void Proc<TParam1, TParam2>(TParam1 param1, TParam2 param2);

    public delegate void Proc<TParam1, TParam2, TParam3>(TParam1 param1, TParam2 param2, TParam3 param3);

    public delegate void Proc<TParam1, TParam2, TParam3, TParam4>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4);

    public delegate void Proc<TParam1, TParam2, TParam3, TParam4, TParam5>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5);


    public delegate TResult Func<TResult>();

    public delegate TResult Func<TResult, TParam1>(TParam1 param1);

    public delegate TResult Func<TResult, TParam1, TParam2>(TParam1 param1, TParam2 param2);

    public delegate TResult Func<TResult, TParam1, TParam2, TParam3>(TParam1 param1, TParam2 param2, TParam3 param3);

    public delegate TResult Func<TResult, TParam1, TParam2, TParam3, TParam4>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4);

    public delegate TResult Func<TResult, TParam1, TParam2, TParam3, TParam4, TParam5>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5);

}
Этот код будет работать в .NET начиная с версии 2.0. В .NET 3.5 появилась такая возможность как LINQ (Language Integrated Queries), которая привнесла в язык C# кое-какие функциональные возможности, среди них можно найти семейство делегатов Func, фактически совпадающее с продемонстрированным, а вот семейства Proc там нет.

Метод Main в C# 3.0 можно переписать в виде (исполняемый код будет совершенно идентичным):
Код:
static void Main(string[] args) {
            var PrintIntString = (Proc<int, string>) delegate(int x, string s) { // явное приведение типа через оператор ()
                Console.WriteLine("Int: {0}, String: {1}", x, s);
            };

            var Sum = (Func<int, int, int>) delegate(int a, int b) {
                return a + b;
            };


            PrintIntString(Sum(10, 23), "Hello world!");

            Console.ReadLine();
        }
Я предпочитаю именно такую нотацию, так как она заметно нагляднее и больше похожа на синтаксис оператора def из Nemerle.


Время жизни таких процедур/функций не ограничено текущим стековым фреймом, как в Делфи, так как при конструировании каждого делегата в фоне создается экземпляр специального автоматически сгенерированного компилятором класса, членом которого этот делегат и является. Объект будет доступен для сборщика мусора когда пропадет пояследняя ссылка на него, зачастую это происходит при выходе из основной функции, в этом случае поведение среды совпадает с семантикой вложенных функций Delphi. При острой жажде знаний утиллита ildasm.exe покажет всю подноготную анонимных делегатов.
1.9K
14 июля 2009 года
GreenRiver
451 / / 20.07.2008
Очень интересная тема, совсем недавно начал активно использовать функциональные возможности в C#, очень упрощает жизнь :).
Для процедур в C# 3.0 можно использовать Action<> и мне почему-то ближе такой синтаксис:
 
Код:
Action<int, string> PrintIntString = (int x, string s) =>
{
   Console.WriteLine("Int: {0}, String: {1}", x, s);
};

Func<int, int, int> Sum = (a, b) =>
{
   return a + b;
};
1.8K
14 июля 2009 года
rSolanov
106 / / 04.05.2005
Интересная получилась статья и очень наглядная.
Почему я об этом спрашиваю, так это просто в рамках одной задачи, которую я выполняю. Есть еще один момент, которые очень интересен. Мы разобрались с работой BackgroundWorker'а, но я обнаружил, что некоторые функции которые прекрасно работают в основном потоке, но если их вынести в отдельный поток, то они перестают работать. Примером того служит работа Interop с вызовом отчета из MS Access'а:
Код:
Access.Application App = null;
                Access.Dao.Database db = null;
                try
                {
                    object ObjOpt = System.Reflection.Missing.Value;
                    App = new Access.Application();
                    App.OpenCurrentDatabase("\\\\Template\\App.accdb", false, null);
                    Access.Dao.DBEngine dbEngine = new Access.Dao.DBEngine();
                    db = dbEngine.OpenDatabase("\\\\Template\\App.accdb", ObjOpt, false, ObjOpt);
                    db.QueryDefs["Отчет"].SQL = "exec dbo.USP_Rep_Trades_Information \"" + Form.DateParam.ToString("dd.MM.yyyy") + "\"";
                    SaveFileDialog SFD = new SaveFileDialog();
                    SFD.Filter = "Файлы ADOBE PDF (*.pdf)|*.pdf|Все файлы (*.*)|*.*";
                    SFD.FileName = SFD.FileName + "Отчет.pdf";
                    if (SFD.ShowDialog(this) == DialogResult.OK)
                    {
                        Cursor = System.Windows.Forms.Cursors.WaitCursor;
                        App.DoCmd.OutputTo(
                                            Microsoft.Office.Interop.Access.AcOutputObjectType.acOutputReport,
                                            "Отчет",
                                            "PDF",
                                            SFD.FileName,
                                            Form.AutoStart,
                                            ObjOpt,
                                            ObjOpt
                                           );
                    };
                }
                catch (Exception Ex)
                {
                    MessageBox.Show(
                                    this,
                                    Ex.Message,
                                    @"Формирование отчета ""Отчет""",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Error
                                    );
                }
                finally
                {
                    if (db != null)
                    {
                        db.Close();
                        db = null;
                    };
                    if (App != null)
                    {
                        App.CloseCurrentDatabase();
                        App = null;
                    };
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    Cursor = System.Windows.Forms.Cursors.Arrow;
                };

При выполнении этого кода отдельным поток возникает ошибка, скриншот которой представлен во вложении.
Ошибка происходит на строчке:
 
Код:
db = dbEngine.OpenDatabase("\\\\Firewall\\Template\\App.accdb", ObjOpt, false, ObjOpt);
5
14 июля 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: GreenRiver
Для процедур в C# 3.0 можно использовать Action<>


Спасибо, не знал. :) Видимо просто привык к собственным классам, которые тащу из .NET 2.0.


Цитата: GreenRiver

и мне почему-то ближе такой синтаксис
 
Код:
Action<int, string> PrintIntString = (int x, string s) =>
{
   Console.WriteLine("Int: {0}, String: {1}", x, s);
};


Синтаксис все равно ужасен. Представьте, что вместо int и string стоит что-то в духе SomeComplexLongNameClass. В таких случаях нотация с var чуточку удобнее так как сразу пишется имя функции:

 
Код:
var PrintIntString = new Action<int, string>((x, s) => {
    Console.WriteLine("Int: {0}, String: {1}", x, s);
});
Однако по прежнему приходится писать типы.

Функцональные возможности (вывод типов) в C# кастрированы до безумия. Для кода
 
Код:
var PrintPair = (x, s) => {
    Console.WriteLine("First: {0}, Second: {1}", x, s);
};
компилятор сообщает следующее:
[quote=csc.exe]
Cannot assign lambda expression to an implicitly-typed local variable [/quote]

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

Исключительно для сравнения приведу, как такие конструкции должны выглядеть по-человечески.
Nemerle:
 
Код:
def PrintPair(x, s) {
    Console.WriteLine($"First: $x, Second: $s");
}
F#:
 
Код:
let PrintPair x s =
    Console.WriteLine("First: {0}, Second: {1}", x, s)
2rSolanov: ваш вопрос вообще-то достоин отдельного топика.
1.8K
14 июля 2009 года
rSolanov
106 / / 04.05.2005
Вы правы, помещу свой вопрос в отельный топик раздела Microsoft .NET Framework
1.9K
14 июля 2009 года
GreenRiver
451 / / 20.07.2008
Действительно лаконично :) хотя в явном указании типа тоже есть свои плюсы - intellisense, отсутствие затрат на вывод типа в ран-тайме!
Кстати возможно dynamic в четвертом C# исправит эту ситуацию - по сути им остается упростить синтаксис, чтобы не указывать dynamic в Func<..> и получится почти полноценный вывод типа?

P.S. давно смотрю в сторону Nemerle (и F# немного ковырял) но все не хватает времени и решимости попробовать их в деле :)
1.9K
14 июля 2009 года
GreenRiver
451 / / 20.07.2008
Делегаты из пространства имен Hardcase.Common.Utils ;) навели на мысль:

C# 4.0
 
Код:
public delegate void Func(dynamic param1, dynamic param2);
...
Func Test = (x, y) => Console.WriteLine("First: {0} Second: {1}", x, y);
Test(1, 3);


Работает и имеет вполне приличный синтаксис :)
5
14 июля 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: GreenRiver
Действительно лаконично :) хотя в явном указании типа тоже есть свои плюсы - intellisense, отсутствие затрат на вывод типа в ран-тайме!

Не нужно только фантазировать - Intellisense работает прекрасно. И типы аргументов в Nemerle и F# выводятся как раз таки на этапе компиляции, в отличие от пресловутых dynamic типов в C# 4.0.

Цитата: GreenRiver

Кстати возможно dynamic в четвертом C# исправит эту ситуацию - по сути им остается упростить синтаксис, чтобы не указывать dynamic в Func<..> и получится почти полноценный вывод типа?

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

<holywar>
Я ортодоксален в вопросе типизации, и dynamic в C# вижу как преступление в отношении языка. Типизация обязана быть статической. Кроме того, существуют более важные парадигмы, которых нехватает C#: контакты из Spec# хотя бы. Вон в Sing# их же реализовали (да этом принципе вообще ОС Singularity построена), и притом очень неплохо.
</holywar>

Цитата: GreenRiver

P.S. давно смотрю в сторону Nemerle (и F# немного ковырял) но все не хватает времени и решимости попробовать их в деле :)

А я уже давно для себя решил - пора потихоньку сползать с C#. Пожалуй единственная красивая и стройная версия языка была C#2.0, в которой появились генерики. После этого начался кошмарный ужас - малополезный LINQ, половинчатое (и оттого корявое) ФП, попахивающее дикой попсятиной dynamic. А реально нужных вещий Хейлсберг в язык не вносит. Обидно, блин.


З.Ы. Статическая типизация для меня как наркотик. Совершенно не могу писать на JavaScript из-за того, что не чувствую опеки компилятора - нет никакого автоматизированного инструмента, способного хоть как-то оценить работоспособность и корректность кода без его запуска.

1.9K
14 июля 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: hardcase
Не нужно только фантазировать Intellisense работает прекрасно. И типы аргументов в Nemerle и F# выводятся как раз таки на этапе компиляции, в отличие от пресловутых dynamic типов в C# 4.0.


Ого, честно говоря я думал, что и в Nemerle и в F# вывод типа в рантайме происходит. Надо будет почитать на эту тему!

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


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

Цитата: hardcase

А я уже давно для себя решил - пора потихоньку сползать с C#. Пожалуй единственная красивая и стройная версия языка была C#2.0, в которой появились генерики. После этого начался кошмарный ужас - малополезный LINQ, половинчатое (и оттого корявое) ФП, попахивающее дикой попсятиной dynamic. А реально нужных вещий Хейлсберг в язык не вносит. Обидно, блин.


На счет 2.0 совершенно согласен, очень завершенным выглядел!
LINQ не пользуюсь, т.к. специфика работы далека от баз данных и запросов к ним... но тоже не понимаю в нем прикола!
А вот ФП мне понравилось, несколько раз уже в проекте было, когда удалось просто и элегантно решить проблему, которую без него конечно тоже можно было решить, но сильно уж извращенчески. Я понимаю, что это только огрызки ФП, но и они бывают порой полезны.
dynamic вообще только сейчас и вспомнил, просто интересно было можно ли заставить скомпилировать такой код =)

В общем надо наверное выделить время и попробовать Nemerle... Хотя мне с шарпа точно ещё рано слазить :)

1.9K
24 июля 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: hardcase

Кроме того, существуют более важные парадигмы, которых нехватает C#: контакты из Spec# хотя бы.



Кажется они решили реализовать контракты на уровне BCL
http://blogs.msdn.com/bclteam/archive/2009/05/22/what-s-new-in-the-bcl-in-net-4-beta-1-justin-van-patten.aspx

5
25 июля 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: GreenRiver
Кажется они решили реализовать контракты на уровне BCL


Пред- и пост- условия, а также инварианты уже давно реализованы в Nemerle (пространство имен Nemerle.Assertions).

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог