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

среда, 8 апреля 2015 г.

Javadoc - наследование комментариев

В C# классы наследуют комментарии из интерфейса.

В JavaDoc всё сложнее - нужно писать над унаследованным свойством:

/**
 * {@inheritDoc}
*/

понедельник, 24 ноября 2014 г.

Удивительные факты о наследовании

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

trait Animal {
  def word: String
  def talk() { println(this.word) }
}
class Cat extends Animal { def word = "Meow!" }

class Dog extends Animal { def word = "Woof!" }

class JustACat { def meow = "Meow!" }

class HappyCat extends JustACat with Animal {
  def word = meow + " :)"
}

class SadCat extends JustACat with Animal {
  def word = meow + " :("
}
val dog = new Dog()
dog.talk

"Вот по большому счету и все, что нужно знать об АТД и классах типов" сообщает нам автор. "Забудьте об ООП! Используйте АТД и тайпклассы!".

Не имею ничего против trait-ов и языка Scala, но такой пример реализации ООП порадует разве что борцов за права домашних животных. Абсолютно непонятно, какое отношение к реальным программам имеют все эти котики и собачки и как собрать из этих грузок и перегрузок что-то работающее. Вспоминать Майерса или Буча тут не стоит - куда уместней общеизвестная песенка:

Я жму на велике любимом,
Он зовется "Украина"
Но седло от унитаза,
А педаль от пианино.
Все потырили друзья,
В двух местах сломали раму,
Ну и пусть себе смеются,
Металлисты-наркоманы!

Раз уж пошла такая путаница, надо разобраться, какое бывает ООП. Обычно под ним понимают что-то в духе C++/C#/Java - модификаторы public/private/protected, виртуальные функции, наследование, и проч., проч., проч.

Но есть языки наподобие Python, Ruby или PHP - где приватных полей нет вообще, а приватность функций зависит от доброй воли пользователя библиотеки, а типы можно сравнивать через duck typing. При этом наследование в них очень разное: в PHP оно аналогично Java (можно наследовать только от одного класса), Python разрешает множественное наследование, а в Ruby разрешает новомодные подмешивания (впрочем, большинство пользователей про них не в курсе).

Есть, наконец, JavaScript, где наследование через прототип настолько загадочно, что некоторые даже пишут свои обёртки, чтобы заменить его на что-то более привычное. И многие из этих обёрток реализуют и множественное тоже.

Ну и на закуску - есть известные реализации псевдо-ООП на C (вроде тех, что встроены в GTK и WinAPI), есть языки-надстройки вроде Lua, и громадную, ещё с начала 1990-х историю ORM для записи унаследованных объектов в реляционные базы данных.

Как легко заметить, главное в ООП - наследование, а наличие приватных полей и виртуальных функций - дело десятая. Куча народу пишет на JavaScript и не в курсе про его систему наследования, или на Python, а наследуют "как привыкли", с интерфейсами и основным классом.

О чём и было сказано уже в другом блоге, где дискуссия продолжалась: Ситуация усугубляется еще и тем, что из широкоизвестных ОО языков чисто ОО языками являются всего два -- Java и Eiffel.

понедельник, 30 июня 2014 г.

CsvHelper

Формат CSV не так прост, как кажется. Формально это просто табличка: столбцы разделены запятыми.

А вот при попытке с ним поработать начинается веселье:

  • Точку в десятичных числах, чтобы разделить разряды, ставят только англосаксы и программисты. Некоторые другие народы могут разделять их запятыми. Американское 10,000,000.03 - это русское 10.000.000,03. И если в системе установлена другая система представления чисел, то Excel сохранит файл, разделив столбцы точками с запятой. Хотя диалог сохранения в Excel будет по прежнему уверять, что сохраняет с запятыми как разделителями
  • CSV - не UNICODE, и по умолчанию сохраняется в текущей кодировке системы.
  • В первой строке - как правило, не данные, а названия столбцов.
Поэтому на C# такие файлы положено читать специальной библиотекой. Текущая версия есть в NUnit, а где-то в версии 3.0 вернутся и атрибуты для маппинга, которые я прикрутил обратно в своём форке.

понедельник, 22 апреля 2013 г.

C#: Checking for null’s with monads


public static TResult With<tinput tresult="">
(this TInput o, Func<tinput tresult=""> evaluator)
where TResult : class
where TInput : class
{
    return (o == null) ? return null : evaluator(o);
}
Usage:
string postCode = this.With(x => person)
 .With(x => x.Address)
 .With(x => x.PostCode);

понедельник, 18 июня 2012 г.

C#: Labels instead of numbers in String.Format

Can be labels used instead of numbers in String.Format?

No, they aren't by default. But it's possible by the very nice Scott Hanselman's extention method for it:

public static class FormattableObject
{
    public static string ToString(this object anObject, string aFormat)
    {
        return FormattableObject.ToString(anObject, aFormat, null);
    }
 
    public static string ToString(this object anObject, string aFormat, IFormatProvider formatProvider)
  {
       StringBuilder sb = new StringBuilder();
     Type type = anObject.GetType();
     Regex reg = new Regex(@"({)([^}]+)(})",RegexOptions.IgnoreCase);
        MatchCollection mc = reg.Matches(aFormat);
      int startIndex = 0;
     foreach(Match m in mc)
      {
           Group g = m.Groups[2]; //it's second in the match between { and }
           int length = g.Index - startIndex -1;
           sb.Append(aFormat.Substring(startIndex,length));
             
            string toGet = String.Empty;
            string toFormat = String.Empty;
         int formatIndex = g.Value.IndexOf(":"); //formatting would be to the right of a :
           if (formatIndex == -1) //no formatting, no worries
          {
               toGet = g.Value;
            }
           else //pickup the formatting
            {
               toGet = g.Value.Substring(0,formatIndex);
               toFormat = g.Value.Substring(formatIndex+1);
            }
 
           //first try properties
          PropertyInfo retrievedProperty = type.GetProperty(toGet);
           Type retrievedType = null;
          object retrievedObject = null;
          if(retrievedProperty != null)
           {
               retrievedType = retrievedProperty.PropertyType;
             retrievedObject  = retrievedProperty.GetValue(anObject,null);
           }
           else //try fields
           {
               FieldInfo retrievedField = type.GetField(toGet);
                if (retrievedField != null)
             {
                   retrievedType = retrievedField.FieldType;
                   retrievedObject = retrievedField.GetValue(anObject);
                }
           }
 
           if (retrievedType != null ) //Cool, we found something
          {
               string result = String.Empty;
               if(toFormat == String.Empty) //no format info
               {
                   result = retrievedType.InvokeMember("ToString",
                     BindingFlags.Public | BindingFlags.NonPublic |
                     BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase
                     ,null,retrievedObject,null) as string;
              }
               else //format info
              {
                   result = retrievedType.InvokeMember("ToString",
                     BindingFlags.Public | BindingFlags.NonPublic |
                     BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase
                     ,null,retrievedObject,new object[]{toFormat,formatProvider}) as string;
             }
               sb.Append(result);
          }
           else //didn't find a property with that name, so be gracious and put it back
            {
               sb.Append("{");
             sb.Append(g.Value);
             sb.Append("}");
         }
           startIndex = g.Index + g.Length +1 ;
        }
       if (startIndex < aFormat.Length) //include the rest (end) of the string
      {
           sb.Append(aFormat.Substring(startIndex));
       }
       return sb.ToString();
   }
}

It's faster then others. Use it with objects:
Person p = new Person();
string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}");

пятница, 8 июня 2012 г.

C#: Difference between && and &

(I'll write in English, because this problem is widely asked.)

What's the difference between && and & or || and | in C#? In C++ it's the difference between logical and bitwise AND.

The most of its usage as connected with universality of if in C++. For example, if(a) in C++ never executes when:
  • a is false (because false = 0)
  • a is null (because null = 0)
  • a is a number <=0.

So, if(a == true) or  (a > 0) is useless in C++. if(a) is much shorter.


JavaScript implemented 1 more option:
  • a is an empty string ("")
C# is a strongly-typed langauge and the if in C# works different. It accepts only boolean values in scope.

So, the only difference between | an || and & and && in C# is the short-circuiting.

i.e.:


if(foo() && boo())


if foo() == false, boo() willn't be executed.


if(foo() & boo())


The foo() and boo() will be executed.


So, never write this way:


if(a != 0 & b / a > 100)


if a == 0, it will fall in Division by DivideByZeroException. The right variant is:


if(a != 0 && b / a > 100)

The same thing:

 if(item != null & item.isActive())

Never write this way. It has to be written like this:


 if(item != null && item.isActive())

пятница, 18 мая 2012 г.

ASP.Net: Текущая страница из ASCX

Как узнать текущую страницу из ASCX? Например, для какой-нибудь компоненты из недр sitecore layout?

Вот так:

String.Format("{0}://{1}{2}",
                Request.ServerVariables["HTTPS"] == "on" ?
                    "https" :
                    "http",
                Request.ServerVariables["SERVER_NAME"],
                Request.RawUrl.ToString());

суббота, 28 апреля 2012 г.

C#: Number Range в Combobox

Например, нам нужно положить в telerik-оподобный control список из вот таких элементов:
< telerik:radcomboboxitem runat="server" text="2010" value="10" >

Такие штуки надо генерировать в одну строку:
private const int CC_VALID_YEARS_RANGE = 9;
....................................................................................
ddlYear.DataSource = Enumerable.Range(DateTime.Today.Year, CC_VALID_YEARS_RANGE)
                               .ToDictionary(item => item.ToString(),
                                             item => (item % 1000).ToString());
ddlYear.DataTextField = "Key";
ddlYear.DataValueField = "Value";
ddlYear.DataBind();
А если надо добавить ещё одну строку Year с пустым значением - нужно предварительно сбросить Dictionary в ToList() (получится List < KeyValuePair < string, string > >) и сделать insert в 0-ой индекс. Дело в том, что Dictionary<> сам по себе не сортируется - соответственно, Insert-а в нём нет и foreach перебирает его в том же порядке, в каком элементы добавились.
private const int CC_VALID_YEARS_RANGE = 9;
....................................................................................
List < nKeyValuePair < string, string > > yearsList =
              Enumerable.Range(DateTime.Today.Year, CC_VALID_YEARS_RANGE)
                        .ToDictionary(item => item.ToString(),
                                      item => (item % 1000).ToString()).ToList();
yearsList.Insert(0, new KeyValuePair < string,string > ("Year", String.Empty));
ddlYear.DataSource = yearsList;
ddlYear.DataTextField = "Key";
ddlYear.DataValueField = "Value";
ddlYear.DataBind();

четверг, 12 апреля 2012 г.

Sitecore: Не искать ли через SQL?

ASP.Net: Ловим Enter на TextBox

Как сделать, чтобы при нажатии Enter на TextBox "нажималась" определённая кнопка? Некоторые до сих пор рекомендуют безумный способ с добавлением невидимого TextBox. Но правильный вариант - это дописывать в Page_Load:

textBox.Attributes.Add("onkeydown", "if(event.which || event.keyCode){if ((event.which == 13) || (event.keyCode == 13)) {document.getElementById('" + buttonDoh.ClientID + "').click();return false;}} else {return true};");

C#: Собираем вместе команды и транзакции

Если нужно отправить в базу много команд - надо сразу делать вот так:

List < sqlcommand >  sqlCommands = new List < sqlcommand > ();
foreach (ParsedAddress parsedAddress in parsedAddresses)
{
    if (parsedAddress.IsError)
        continue;

    SqlCommand sqlCommand = new SqlCommand(sqlcmd_SAVE_PARSED_VALUES);
    sqlCommand.Parameters.Add(new SqlParameter("Param1", "Param1"));
    SqlParameter NullableParameter = new SqlParameter("NullableParameter", SqlDbType.Int);
    NullableParameter.IsNullable = true;
    if (itemToWrite.NullableValue != null)
        NullableParameter.Value = itemToWrite.NullableValue.ID;
    else
        NullableParameter.Value = DBNull.Value;
    sqlCommand.Parameters.Add(NullableParameter);
    sqlCommand.Parameters.Add(new SqlParameter("Param2", "Param2"));
    sqlCommand.Parameters.Add(new SqlParameter("Param3", 13));
    sqlCommands.Add(sqlCommand);
}
SqlConnection connection = new SqlConnection(connectionStringWrite);
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
try
{
    foreach (SqlCommand cmd in sqlCommands)
    {
        cmd.Connection = connection;
        cmd.Transaction = transaction;
        cmd.ExecuteNonQuery();
        cmd.Dispose();
    }
    transaction.Commit();
}
catch (SqlException sqlError)
{
    transaction.Rollback();
    throw sqlError;
}
finally
{
    connection.Close();
}

Тогда в случае exception-а произойдёт Rollback и ничего не случится.

А Exception уйдёт "наверх", где его можно будет обработать.

среда, 11 апреля 2012 г.

C#: List<> в DataGrid

Когда-то List<> скидывали в DataGrid вот так:

dataGridView.DataSource = null;
dataGridView.DataSource = list;

Но увы - если загружать list таким образом, в ViST2008 мы будем получать IndexOutOfRangeException каждый раз, когда попытаемся его выделить. :(

Поэтому надо привязывать BindingList, который умеет обновляться автоматически. А обновлять только ширину колонок:

dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader);

пятница, 4 ноября 2011 г.

C#: Пишем в Control из Thread

Решений немало.

Простое, как грабли, через делегат:
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
    someLabel.Text = newText; // runs on UI thread
});

То же самое, вынесено в отдельную функцию и выверено по MSDN:

delegate void UpdateLabelDelegate (string message);

void UpdateLabel (string message)
{
    if (InvokeRequired)
    {
         Invoke (new UpdateLabelDelegate (UpdateLabel), new object[] { message });
         return;
    }
    MyLabelControl.Text = message;
}

Там же - замечательные LINQ-оподобные конструкции, которые не так просто вставить в Blogger - там есть Template-овы конструкции вроде < Func <, а Blogger принимает их за теги. Поэтому не забываем ставить пробелы: Очаровательное LINQ-оподобное решение:

private delegate void SetPropertyThreadSafeDelegate(Control @this, Expression< Func > property, TResult value);

public static void SetPropertyThreadSafe(this Control @this, Expression< Func > property, TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;

if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}

if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate(SetPropertyThreadSafe), new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(propertyInfo.Name, BindingFlags.SetProperty, null, @this, new object[] { value });
}
}

Улучшенные вариант с проверкой на null и более жёсткой типизацией:

public static void SetPropertyInGuiThread< C, V >(this C control, Expression< Func < C, V >> property, V value) where C : Control
{
    var memberExpression = property.Body as MemberExpression;
    if (memberExpression == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
        throw new ArgumentException("The 'member' expression must specify a property on the control.");

    if (control.InvokeRequired)
        control.Invoke(
            (Action< C, Expression< Func < C, V >>, V>)SetPropertyInGuiThread,
            new object[] { control, property, value }
        );
    else
        propertyInfo.SetValue(control, value, null);
}

понедельник, 31 октября 2011 г.

C#: Enum в ComboBox

Сбросить Enum в Combobox можно одной строкой:
comboBox.DataSource = Enum.GetValues(typeof(AnEnumType));

И выделяем:
comboBox.SelectedItem = AnEnumType.Value