В C# для работы с битовыми полями предусмотрен класс BitVector32. Этот класс более эффективен, чем BitArray.
using System;
using System.Collections.Specialized;
namespace BitVector32Test
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
BitVector32 vector1 = new BitVector32( 0 );
// Маски для первых пяти битов
int bit1 = BitVector32.CreateMask();
int bit2 = BitVector32.CreateMask(bit1);
int bit3 = BitVector32.CreateMask(bit2);
int bit4 = BitVector32.CreateMask(bit3);
int bit5 = BitVector32.CreateMask(bit4);
// Получаем BitVector32{00000000000000000000000000000000}
Console.WriteLine(vector1.ToString());
vector1[bit1] = true; // установить 1 бит
// Получаем BitVector32{00000000000000000000000000000001}
Console.WriteLine(vector1.ToString());
vector1[bit3] = true; // установить 3 бит
// Получаем BitVector32{00000000000000000000000000000101}
Console.WriteLine(vector1.ToString());
vector1[bit5] = true; // установить 5 бит
// Получаем BitVector32{00000000000000000000000000010101}
Console.WriteLine(vector1.ToString());
BitVector32 vector2 = new BitVector32( 0 );
// Создаем 4 секции. Первая имеет максимум
// значений 6, т. е занимает 4 бита. Вторая - максимум 3
// (т. е. два бита), затем 1 бит, затем 4 бита.
BitVector32.Section sect1 = BitVector32.CreateSection( 6 );
BitVector32.Section sect2 =
BitVector32.CreateSection( 3, sect1 );
BitVector32.Section sect3 =
BitVector32.CreateSection( 1, sect2 );
BitVector32.Section sect4 =
BitVector32.CreateSection(15, sect3 );
vector2[sect2] = 3;
// Получим BitVector32{00000000000000000000000000011000}
Console.WriteLine(vector2.ToString());
vector2[sect4] = 1;
// Получим BitVector32{00000000000000000000000001011000}
Console.WriteLine(vector2.ToString());
}
}
}
Saturday, December 4, 2010
Friday, December 3, 2010
Thread-safe словарь
В .Net до версии 3 приходилось словари, доступные для работы в потоках, делать руками. В версии 4 появился удобный класс ConcurrentDictionary. Он находится в пространстве имен System.Collections.Concurrent. Правда к его методам нужно привыкнуть. Например, вместо простого Add здесь bool TryAdd(), который вернет false, если такой ключ уже есть. Аналогично - TryRemove, TryGetValue, TryUpdate.
Обновить значения в hashtable в цикле
Если попробовать изменять значения hashtable во время перебора элементов в цикле, то будет сгенерировано исключение.
Обойти это можно так:
foreach (object key in new List<object>(dataTable.Keys.Cast<object>()))
dataTable[key] = null;
Обойти это можно так:
foreach (object key in new List<object>(dataTable.Keys.Cast<object>()))
dataTable[key] = null;
Wednesday, December 1, 2010
Цитата
В стрессовой ситуации для того, чтобы сохранить спокойствие, достаточно создать в Word файл "спокойствие" и кликнуть на кнопку "сохранить".
Какая разница между оператором as и прямым приведением типа
В C# существует два способа приведения типов: оператор as и прямое приведение типа. Отличие этих способов том, что при невозможности прямого приведения типов будет сгенерировано исключение InvalidCastException, тогда как оператор as просто вернет null.
Вполне вероятно, что после получения нулевого указателя от оператора as, где-то в коде будет сгенерировано исключение NullReferenceException (если получение null не предусматривалось логикой программы). Т. о. использование прямого приведения предпочтительнее, т. к. найти ошибку в этом случае значительно проще, чем искать безымянный NullReferenceException.
Вполне вероятно, что после получения нулевого указателя от оператора as, где-то в коде будет сгенерировано исключение NullReferenceException (если получение null не предусматривалось логикой программы). Т. о. использование прямого приведения предпочтительнее, т. к. найти ошибку в этом случае значительно проще, чем искать безымянный NullReferenceException.
Как определить, какие версии .NET Framework установлены в системе и установлены ли пакеты обновлений
В статье MSDN http://support.microsoft.com/default.aspx/kb/318785 содержится информация как определить установленные версии .NET Framework. В частности указывается, что каждая версия создает отдельную папку в каталоге %systemroot%\Microsoft.NET\Framework. Имя папки имеет вид vN.N.NXXXX. Конкретную версию можно определить по версии системного модуля Mscorlib.dll.
Задача для архитектора (часть 3)
С классами теперь все просто.
Receipt – класс рецептуры. Атрибуты над полями нужны для загрузки данных из БД, про это как-нибудь в другой раз.
public class Receipt : Entity
{
/// <summary>
/// Дата создания
/// </summary>
[FieldName("CreatedDate")]
public DateTime CreatedDate { get; set; }
/// <summary>
/// Название
/// </summary>
[FieldName("Title")]
public string Title { get; set; }
/// <summary>
/// Описание
/// </summary>
[FieldName("Description")]
public string Description { get; set; }
/// <summary>
/// XML описание команд
/// </summary>
[FieldName("Commands")]
public string Commands { get; set; }
}
Для команд я ввел перечисление, хранящее типы команд:
public enum ReceiptCommandType
{
[ViewableEnumMember("Загрузка компонента")]
Loading = 1,
}
Теперь надо разобраться с классом команды. Очевидно, что нужно сделать базовый класс для команд:
public abstract class BaseReceiptCommand
{
private ReceiptCommandType commandType;
private string commandTypeName;
public BaseReceiptCommand(ReceiptCommandType commandType, XElement element)
{
Init(commandType);
if (element != null)
LoadFromXML(element);
}
public BaseReceiptCommand(ReceiptCommandType commandType) :
this(commandType, null)
{
}
public abstract void LoadFromXML(XElement element);
private void Init(ReceiptCommandType commandType)
{
this.commandType = commandType;
this.commandTypeName = EnumHelper<ReceiptCommandType>.GetName(commandType);
}
public ReceiptCommandType CommandType
{
get
{
return commandType;
}
}
public string CommandTypeName
{
get
{
return commandTypeName;
}
}
public virtual XElement ToXml()
{
return new XElement("command", new XAttribute("type", CommandType));
}
public static ReceiptCommandType GetTypeFromXML(XElement element)
{
return (ReceiptCommandType)Enum.Parse(typeof(ReceiptCommandType), element.Attribute("type").Value);
}
public override string ToString()
{
return "Команда";
}
public abstract string ToFullString();
}
По идее, тип команды определяется самим классом команды и нужды в отдельном поле нет, но мне не хотелось завязывать имена классов на типы команд именно таким образом, поэтому я добавил поле типа в сам класс команды. Хотя, конечно, связать их нужно и я сделал это с помощью фабрики:
public static class ReceiptCommandFactory
{
public static BaseReceiptCommand GetReceiptCommand(ReceiptCommandType commandType)
{
return GetReceiptCommand(commandType, null);
}
public static BaseReceiptCommand GetReceiptCommand(ReceiptCommandType commandType, XElement element)
{
switch (commandType)
{
case ReceiptCommandType.Loading:
return new LoadReceiptCommand(element);
default:
throw new ApplicationException("ReceiptCommandFactory::GetReceiptCommand");
}
}
}
Конструкторов у класса команды получилось два – один создает пустую (новую) команду, второй – загружает ее из XML. Обратите внимание на абстрактный метод LoadFromXML. Он заставляет все классы, наследующие от базового, реализовывать собственный метод загрузки параметров команды (об этом я потом расскажу отдельно). Метод абстрактный, т.к. каждая команда сама решает, как ей загрузить себя из XML.
Кроме поля типа команды мне потребовалось поле имени (названия) этой команды. Это легко было сделать, получив атрибут соответствующей команды. Поменять тип команды на лету нельзя, поэтому вполне достаточно прочитать ее имя в конструкторе.
Ну и еще, при чтении XML мне потребовалось заранее знать тип команды, поэтому нужен был отдельный метод для чтения типа.
Вот как выглядит чтение и сохранение команд в XML:
public static class ReceiptXMLHelper
{
/// <summary>
/// Сохранение списка команд в XML
/// </summary>
public static XDocument ReceiptCommandListToXml(IList commandList)
{
XDocument document = new XDocument();
XElement root = new XElement("root");
document.Add(root);
foreach (BaseReceiptCommand command in commandList)
{
root.Add(command.ToXml());
}
return document;
}
/// <summary>
/// Получение списка команд
/// </summary>
public static List<BaseReceiptCommand> GetReceiptCommandList(Receipt receipt)
{
List<BaseReceiptCommand> result = new List<BaseReceiptCommand>();
if (receipt != null)
{
if (!string.IsNullOrEmpty(receipt.Commands))
{
XDocument document = XDocument.Parse(receipt.Commands);
return GetReceiptCommandListFromXml(document);
}
}
return result;
}
/// <summary>
/// Восстановление списка команд из XML
/// </summary>
public static List<BaseReceiptCommand> GetReceiptCommandListFromXml(XDocument document)
{
List<BaseReceiptCommand> result = new List<BaseReceiptCommand>();
foreach (var command in document.Root.Elements())
{
ReceiptCommandType type = BaseReceiptCommand.GetTypeFromXML(command);
result.Add(ReceiptCommandFactory.GetReceiptCommand(type, command));
}
return result;
}
}
Ничего сложного тут нет. При сохранении в XML мы бежим по списку команд и каждой команде “говорим” сохранить себя в XML. При чтении – обратная операция, только сначала нам нужно определиться с типом создаваемой команды, запросить этот тип у фабрики и затем передать команде XML, чтобы она прочитала из него свои параметры.
А вот как выглядит команда "загрузка компонента":
public class LoadReceiptCommand : BaseReceiptCommand
{
static CultureInfo cultureEnUS = new CultureInfo("en-US");
public LoadReceiptCommand() : base (ReceiptCommandType.Loading)
{
}
public LoadReceiptCommand(XElement element)
: base(ReceiptCommandType.Loading, element)
{
}
public Int64 ComponentId
{
get
{
if (componentRecord != null)
return componentRecord.Id;
else
return -1;
}
set
{
if ((componentRecord == null) || (componentRecord.Id != value))
{
componentRecord = MainBLL.Instance.GetComponent(value);
}
}
}
public double Weight
{
get; set;
}
private Entities.Component componentRecord = null;
public string ComponentName
{
get
{
if (componentRecord == null)
return "(не задан)";
return componentRecord.ComponentName;
}
}
public override void LoadFromXML(XElement element)
{
ComponentId = -1;
XAttribute componentAttr = element.Attribute("componentId");
if (componentAttr != null)
{
ComponentId = Int64.Parse(componentAttr.Value);
}
XAttribute weightAttr = element.Attribute("weight");
if (weightAttr != null)
{
Weight = double.Parse(weightAttr.Value, cultureEnUS);
}
}
public override XElement ToXml()
{
XElement result = base.ToXml();
result.Add(new XAttribute("componentId", ComponentId));
result.Add(new XAttribute("weight", Weight));
return result;
}
public override string ToString()
{
return "Загрузка компонента " + ComponentName;
}
public override string ToFullString()
{
return string.Format("Загрузка [{0}]: {1} кг", ComponentName, Weight);
}
}
Команда загрузки компонента имеет два дополнительных поля: ComponentId (Id загружаемого компонента), Weight (сколько нужно загружать). Причем, первое поле на самом деле загружает собственно запись компонента, полученную из таблицы компонентов. При отображении нам потребуется не Id, а поле ComponentName, возвращающее название компонента.
Сохранение и чтение из XML просто записывает и читает все нужные поля из XML.
В следующей части я расскажу, как просто и быстро сделать редактирование списка команд.
Subscribe to:
Posts (Atom)