Проблема, с которой вы сталкиваетесь, связана с тем, как числа с плавающей запятой представлены на компьютере. Более подробное обсуждение представлений с плавающей запятой появляется к концу моего ответа (раздел «Плавающее представление»). Версия TL; DR: поскольку компьютеры имеют ограниченное количество памяти, цифры могут быть представлены только с конечной точностью. Таким образом, точность чисел с плавающей запятой ограничена определенным количеством десятичных знаков (около 16 значащих цифр для значений с двойной точностью , по умолчанию используется в MATLAB).
Теперь, чтобы обратиться к конкретному примеру в вопросе ... в то время как 24.0000
и 24.0000
отображаются в виде таким же образом, оказывается, что в этом случае они фактически отличаются очень маленькими десятичными суммами. Вы не видите этого, потому что MATLAB показывает только 4 значащие цифры по умолчанию , сохраняя общий дисплей аккуратным и аккуратным. Если вы хотите увидеть полную точность, вы должны либо выдать команду format long
, либо просмотреть шестнадцатеричное представление числа:
>> pi
ans =
3.1416
>> format long
>> pi
ans =
3.141592653589793
>> num2hex(pi)
ans =
400921fb54442d18
Поскольку существует только конечное число значений, которые могут быть представлены для числа с плавающей запятой, возможно, чтобы вычисление привело к значению, которое попадает между двумя из этих представлений. В таком случае результат должен быть округлен до одного из них. Это приводит к небольшой ошибке машинной точности . Это также означает, что инициализация значения напрямую или некоторым вычислением может дать несколько иные результаты. Например, значение 0.1
не имеет точного представления с плавающей запятой (т. Е. Оно слегка округляется), и поэтому вы получаете контр-интуитивные результаты, подобные этому из-за способа ошибки округления:
>> a=sum([0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]); % Sum 10 0.1s
>> b=1; % Initialize to 1
>> a == b
ans =
logical
0 % They are unequal!
>> num2hex(a) % Let's check their hex representation to confirm
ans =
3fefffffffffffff
>> num2hex(b)
ans =
3ff0000000000000
Поскольку значения с плавающей запятой могут отличаться очень небольшими суммами, любые сравнения должны выполняться путем проверки что значения находятся в некотором диапазоне (то есть толерантности) друг от друга, а не в точности равны друг другу. Например:
a = 24;
b = 24.000001;
tolerance = 0.001;
if abs(a-b) < tolerance, disp('Equal!'); end
отобразит «Равно!».
Затем вы можете изменить свой код на что-то вроде:
points = points((abs(points(:,1)-vertex1(1)) > tolerance) | ...
(abs(points(:,2)-vertex1(2)) > tolerance),:)
Хороший обзор чисел с плавающей запятой (и, в частности, стандарта IEEE 754 для арифметики с плавающей запятой ) Что должен знать каждый компьютерный ученый Арифметика с плавающей запятой Дэвида Голдберга
Бинарное число с плавающей запятой фактически представлено тремя целыми числами: знаковым битом s
, значением (или коэффициентом / фракцией) b
, и экспонентой e
. Для формата с плавающей запятой с двойной точностью каждое число представлено 64 битами, выложенными в памяти следующим образом:
Реальное значение может быть найдено по следующей формуле:
Этот формат допускает числовые представления в диапазоне от 10 ^ -308 до 10 ^ 308. Для MATLAB вы можете получить эти ограничения из realmin
и realmax
:
>> realmin
ans =
2.225073858507201e-308
>> realmax
ans =
1.797693134862316e+308
Поскольку существует конечное число бит, используемых для представляют число с плавающей запятой, существует только так много конечных чисел, которые могут быть представлены в пределах указанного выше диапазона. Вычисления часто приводят к значению, которое точно не соответствует одному из этих конечных представлений, поэтому значения должны округляться. Эти ошибки машинной точности проявляются по-разному, как описано в приведенных выше примерах.
Чтобы лучше понять эти ошибки округления, полезно посмотреть на относительные точность с плавающей запятой, предоставляемая функцией eps
, которая количественно определяет расстояние от заданного числа до следующего по величине представления с плавающей запятой:
>> eps(1)
ans =
2.220446049250313e-16
>> eps(1000)
ans =
1.136868377216160e-13
Обратите внимание, что точность относительный к размеру представленного числа; большие числа будут иметь большие расстояния между представлениями с плавающей запятой и, следовательно, будут иметь меньше цифр точности после десятичной точки. Это может быть важным соображением при некоторых расчетах. Рассмотрим следующий пример:
>> format long % Display full precision
>> x = rand(1, 10); % Get 10 random values between 0 and 1
>> a = mean(x) % Take the mean
a =
0.587307428244141
>> b = mean(x+10000)-10000 % Take the mean at a different scale, then shift back
b =
0.587307428244458
Обратите внимание, что когда мы смещаем значения x
из диапазона [0 1]
в диапазон [10000 10001]
, вычисляем среднее значение, а затем вычитаем среднее смещение для сравнения , мы получаем значение, которое отличается для последних 3 значащих цифр. Это иллюстрирует, как смещение или масштабирование данных могут изменять точность выполненных на нем вычислений, что должно быть связано с определенными проблемами.
Существует отличная разница между коллекциями и ссылками в качестве свойств навигации. Ссылка - объект. Коллекции содержат объекты . Это означает, что инициализация коллекции бессмысленна с точки зрения бизнес-логики: она не определяет связь между сущностями. Установка ссылки делает.
Итак, это исключительно вопрос предпочтения, независимо от того, вы или нет инициализируете встроенные списки.
Что касается «как», некоторые люди предпочитают ленивую инициализацию :
private ICollection<Address> _addresses;
public virtual ICollection<Address> Addresses
{
get { return this._addresses ?? (this._addresses = new HashSet<Address>());
}
Он предотвращает ненужные ссылочные исключения, поэтому он облегчает модульное тестирование и управление сборкой, но также предотвращает ненужную инициализацию. Последнее может иметь значение, когда класс имеет относительно много коллекций. Недостатком является то, что требуется относительно много сантехники, особенно. по сравнению с авто свойствами без инициализации. Кроме того, появление оператора null-распространения в C # сделало менее актуальным инициализацию свойств коллекции.
... если не применена явная загрузка
Единственное, что инициализация коллекции затрудняют проверку того, была ли коллекция загружена Entity Framework. Если коллекция инициализирована, такой оператор, как ...
var users = context.Users.ToList();
... создаст объекты User
, имеющие пустые, а не нулевые Addresses
коллекции (отложенная загрузка). Для проверки того, загружена ли сборка, необходим код, например ...
var user = users.First();
var isLoaded = context.Entry(user).Collection(c => c.Addresses).IsLoaded;
Если сбор не инициализирован, будет выполнена простая проверка null
. Поэтому, когда выборочная явная загрузка является важной частью вашей практики кодирования, то есть ...
if (/*check collection isn't loaded*/)
context.Entry(user).Collection(c => c.Addresses).Load();
... может быть удобнее не инициализировать свойства коллекции.
Справочные свойства являются объектами, поэтому назначение пустого объекта для них имеет смысл.
Хуже того, если вы инициируете их в конструкторе, EF не будет перезаписывать их при материализации вашего объекта или ленивой загрузкой. Они всегда будут иметь свои начальные значения, пока вы активно не замените их. Хуже того, вы даже можете сохранить пустые объекты в базе данных!
И есть еще один эффект: fixup не будет возникать. Фиксация связей - это процесс, посредством которого EF соединяет все объекты в контексте с помощью своих свойств навигации. Когда User
и Licence
загружаются отдельно, все еще User.License
будет заполняться и наоборот. Если, конечно, если License
не был инициализирован в конструкторе. Это справедливо и для 1: n ассоциаций. Если Address
инициализирует User
в своем конструкторе, User.Addresses
не будет заполнен!
Ядро Entity Framework
Фиксация связей в ядре Entity Framework (2.1 в то время записи) не влияет на инициализированные свойства ссылочной навигации в конструкторах. То есть, когда пользователи и адреса извлекаются из базы данных отдельно, свойства навигации заполняются. Однако ленивая загрузка делает не перезаписывать инициализированные свойства ссылочной навигации. Итак, в заключении, а также в EF-ядро инициализирует ссылки навигационных свойств в конструкторах могут вызвать проблемы. Не делай этого. Это не имеет никакого смысла,
Это избыточно для списка new
, так как ваш POCO зависит от Lazy Loading.
Lazy loading - это процесс, при котором сущность или совокупность объектов автоматически загружается из базы данных первый раз, когда осуществляется доступ к объекту, относящемуся к объекту / объектам. При использовании типов сущностей POCO, ленивая загрузка достигается путем создания экземпляров производных прокси-типов, а затем переопределения виртуальных свойств для добавления загрузочного крючка.
blockquote>Если вы удалили виртуальный модификатор, вы отключили бы ленивую загрузку, и в этом случае ваш код больше не будет работать (потому что ничего не будет инициализировать список).
Обратите внимание, что Lazy Loading - это функция, поддерживаемая инфраструктурой сущности, если вы создаете класс вне контекста DbContext, то зависящий код, очевидно, будет страдать от
NullReferenceException
НТН
Я использую ответ из этого Почему мой код Entity Framework Code First proxy collection null и почему я не могу его установить?
Были проблемы с инициализацией конструктора. Единственная причина, по которой я это делаю, - сделать тестовый код проще. Убедившись, что коллекция никогда не имеет значения null, я постоянно меняю инициализацию в тестах и т. Д.
First example is possible to have when creation of these entities is responsibility of third-part code
и test-entity
? И не могли бы вы добавить свой адрес электронной почты в свой профиль. Спасибо снова.
– Iman Mahmoudinasab
2 January 2014 в 17:35
test-entity
является фиктивным или заглушкой, который вы используете в своих модульных тестах. В этом случае third-part code
является ORM. Итак, First example is possible
, когда вы используете ORM, который инициализирует ваши сущности / коллекции, когда у вас есть общая инфраструктура, которая гарантирует, что коллекции не равны нулю.
– Ilya Palkin
2 January 2014 в 19:12
Другие ответы полностью отвечают на вопрос, но я хотел бы добавить что-то, потому что этот вопрос по-прежнему имеет значение и появляется в поиске Google.
Когда вы используете «первую модель кода из базы данных», мастера в Visual Studio все коллекции инициализируются следующим образом:
public partial class SomeEntity
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public SomeEntity()
{
OtherEntities = new HashSet<OtherEntity>();
}
public int Id { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<OtherEntity> OtherEntities { get; set; }
}
Я склонен выводить мастер-файл, поскольку в основном это официальная рекомендация от Microsoft, поэтому почему я добавляю этот пятилетний вопрос , Поэтому я бы инициализировал все коллекции как HashSet
s.
И лично я думаю, что было бы неплохо настроить это, чтобы воспользоваться инициализаторами автоистории C # 6.0:
public virtual ICollection<OtherEntity> OtherEntities { get; set; } = new HashSet<OtherEntity>();
null
, либо ссылаться на осмысленные объекты, которые установлены специально. – Gert Arnold 1 January 2014 в 23:08set
не включено? – user919426 11 July 2017 в 10:12