Upload
andrey-akinshin
View
2.281
Download
3
Embed Size (px)
Citation preview
Об особенностях использования значимых типов в .NET
Андрей Акиньшин
Барнаульское сообщество .NET разработчиков
bug.ineta.ruwww.facebook.com/groups/dotnetbarnaul/
Структуры
• Копирование по значению• Поддержка boxed и unboxed формы• Всегда инициализированы• Методы из Equals, GetHashCode из System.ValueType• Не могут быть базовыми типами
Изменяемые значимые типы
public struct Point{
public int X, Y;public void Move(int dx, int dy){
X += dx;Y += dy;
}}public class Circle{
public Point Center; // Поле}var circle = new Circle();circle.Center = new Point { X = 0, Y = 0 };circle.Center.Move(5, 5);Console.WriteLine(circle.Center.X);
Изменяемые значимые типы
public struct Point{
public int X, Y;public void Move(int dx, int dy){
X += dx;Y += dy;
}}public class Circle{
public Point Center { get; set; } // Свойство}var circle = new Circle();circle.Center = new Point { X = 0, Y = 0 };circle.Center.Move(5, 5);Console.WriteLine(circle.Center.X);
Изменяемые значимые типы
Будут проблемы:// 1. Propertypublic class Circle{
public Point Center { get; set; }}// 2. Readonly fieldpublic class Circle{
public readonly Point Center = new Point();}// 3. IListvar points = new List<Point>();
Изменяемые значимые типы
public struct Enumerator : IEnumerator<T>, IDisposable,IEnumerator
var x = new{
Items = new List<int> { 1, 2, 3 }.GetEnumerator()};
while (x.Items.MoveNext())Console.WriteLine(x.Items.Current);
Изменяемые значимые типы
struct Disposable : IDisposable{
public bool Disposed { get; private set; }public void Dispose() { Disposed = true; }
}
var d = new Disposable();using (d){
// Some code}Console.WriteLine(d.Disposed);
Упаковка и распаковка
// 1var arrayList = new ArrayList();var p = new Point();arrayList.Add(p); // Упаковкаp = (Point) arrayList[0]; // Распаковка
// 2Int32 x = 5;Object o = x; // УпаковкаInt16 y = (Int16) o; // InvalidCastExceptionInt16 z = (Int16)(Int32) o; // Распаковка и приведение
// 3Int32 x = 1;Object y = x;x = 2;Console.WriteLine(x + "/" + (Int32)y);// "2/1"
Упаковка и распаковка
// 1var arrayList = new ArrayList();var p = new Point();arrayList.Add(p); // Упаковкаp = (Point) arrayList[0]; // Распаковка
// 2Int32 x = 5;Object o = x; // УпаковкаInt16 y = (Int16) o; // InvalidCastExceptionInt16 z = (Int16)(Int32) o; // Распаковка и приведение
// 3Int32 x = 1;Object y = x;x = 2;Console.WriteLine(x + "/" + (Int32)y);// "2/1"
Упаковка и распаковка
// 1var arrayList = new ArrayList();var p = new Point();arrayList.Add(p); // Упаковкаp = (Point) arrayList[0]; // Распаковка
// 2Int32 x = 5;Object o = x; // УпаковкаInt16 y = (Int16) o; // InvalidCastExceptionInt16 z = (Int16)(Int32) o; // Распаковка и приведение
// 3Int32 x = 1;Object y = x;x = 2;Console.WriteLine(x + "/" + (Int32)y);// "2/1"
Упаковка и распаковка
interface IChangeable{
void Change(int x, int y);}
struct Point : IChangeable{
public int X, Y;
public void Change(int x, int y){
X = x;Y = y;
}
public override string ToString(){
return X + "," + Y;}
}
Упаковка и распаковка
var p = new Point {X = 1, Y = 1};Console.WriteLine(p);
p.Change(2, 2);Console.WriteLine(p);
Object o = p;Console.WriteLine(o);
((Point) o).Change(3, 3);Console.WriteLine(o);
((IChangeable)p).Change(4, 4);Console.WriteLine(p);
((IChangeable)o).Change(5, 5);Console.WriteLine(o);
Конструкторы по умолчанию
• .NET поддерживает конструкторы по умолчанию дляструктур
• А C# — нет• Но мы всё равно создадим структуру с конструкторомпо умолчанию, которую назовём MyStruct
Конструкторы по умолчанию
• .NET поддерживает конструкторы по умолчанию дляструктур
• А C# — нет
• Но мы всё равно создадим структуру с конструкторомпо умолчанию, которую назовём MyStruct
Конструкторы по умолчанию
• .NET поддерживает конструкторы по умолчанию дляструктур
• А C# — нет• Но мы всё равно создадим структуру с конструкторомпо умолчанию, которую назовём MyStruct
Конструкторы по умолчанию
// Вспомогательные методыstatic T CreateAsDefault<T>() { return default(T); }static T CreateWithNew<T>() where T : new() { return new T(); }
// Вызываетсяvar m = Activator.CreateInstance(typeof(MyStruct));var m = new MyStruct();
// Не вызываетсяvar m = default(MyStruct);var m = CreateWithNew<MyStruct>();var m = CreateAsDefault<MyStruct>();var array = new MyStruct[100];
GetHashCode()
• Быстрая версия(Структура не имеет ссылочных полей, а между еёполями нет свободного места)Используем Xor каждых 4 байта структуры
• Медленная версияИспользуем GetHashCode первого нестатичного поля
GetHashCode()
var a1 = new KeyValuePair<int, int>(1, 2);var a2 = new KeyValuePair<int, int>(1, 3);Console.WriteLine(a1.GetHashCode()); // 1033533110Console.WriteLine(a2.GetHashCode()); // 1033533111
var b1 = new KeyValuePair<int, string>(1, "x");var b2 = new KeyValuePair<int, string>(1, "y");Console.WriteLine(b1.GetHashCode()); // -1888265882Console.WriteLine(b2.GetHashCode()); // -1888265882
Equals()
public override bool Equals (Object obj) {// ...FieldInfo[] thisFields = thisType.GetFields(
BindingFlags.Instance |BindingFlags.Public |BindingFlags.NonPublic);
for (int i=0; i<thisFields.Length; i++) {thisResult = ((RtFieldInfo)thisFields[i]).
InternalGetValue(thisObj,false);thatResult = ((RtFieldInfo)thisFields[i]).
InternalGetValue(obj, false);if (thisResult == null) {
if (thatResult != null)return false;
} else if (!thisResult.Equals(thatResult))return false;
}return true;
}
Equals()var redName = Color.Red;var redArgb = Color.FromArgb(255, 255, 0, 0);Console.WriteLine(redName == redArgb);
public struct Color {private readonly long value;private readonly string name;private readonly short knownColor, state;
public static bool operator ==(Color left, Color right) {if (left.value == right.value &&
left.state == right.state &&left.knownColor == right.knownColor) {
if (left.name == right.name)return true;
if (left.name == (object) null ||right.name == (object) null)
return false;return left.name.Equals(right.name);
}return false;
}}
Equals()var redName = Color.Red;var redArgb = Color.FromArgb(255, 255, 0, 0);Console.WriteLine(redName == redArgb);public struct Color {
private readonly long value;private readonly string name;private readonly short knownColor, state;
public static bool operator ==(Color left, Color right) {if (left.value == right.value &&
left.state == right.state &&left.knownColor == right.knownColor) {
if (left.name == right.name)return true;
if (left.name == (object) null ||right.name == (object) null)
return false;return left.name.Equals(right.name);
}return false;
}}
Размещение в памяти
public struct S1{
public byte Byte1;public int Int1;
}
public struct S2{
public byte Byte1;public byte Byte2;public byte Byte3;public byte Byte4;public int Int1;
}
Console.WriteLine(Marshal.SizeOf(typeof(S1)));Console.WriteLine(Marshal.SizeOf(typeof(S2)));
Размещение в памяти
[StructLayout(LayoutKind.Explicit)]struct MyStruct{
[FieldOffset(0)]public Int16 Value;[FieldOffset(0)]public Byte LowByte;
}
var s = new MyStruct();s.Value = 256 + 100;Console.WriteLine(s.LowByte); // 100
Размещение в памяти
namespace System.Drawing {public struct Color {
/*** Shift count and bit mask for A, R, G, B* components in ARGB mode!*/
private const int ARGBAlphaShift = 24;private const int ARGBRedShift = 16;private const int ARGBGreenShift = 8;private const int ARGBBlueShift = 0;
/// WARNING!!! WARNING!!! WARNING!!! WARNING!!!/// WARNING!!! WARNING!!! WARNING!!! WARNING!!!/// We can never change the layout of this class (adding/// or removing or changing the order of member variables)/// if you want to be compatible v1.0 version of the runtime./// This is so that we can push into the runtime a custom/// marshaller for OLE_COLOR to Color.
}}
Хорошие книжки
Блоги
• http://msdn.microsoft.com/magazine/• http://www.rsdn.ru/• http://blogs.msdn.com/b/ericlippert/• http://sergeyteplyakov.blogspot.ru/• http://timyrguev.blogspot.ru/• http://aakinshin.blogspot.ru/
Спасибо за внимание!