ݺߣ

ݺߣShare a Scribd company logo
Об особенностях использования значимых типов в .NET
Андрей Акиньшин

Барнаульское сообщество .NET разработчиков
bug.ineta.ru
www.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. Property
public class Circle
{
public Point Center { get; set; }
}
// 2. Readonly field
public class Circle
{
public readonly Point Center = new Point();
}
// 3. IList
var 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);
Упаковка и распаковка
// 1
var arrayList = new ArrayList();
var p = new Point();
arrayList.Add(p);
// Упаковка
p = (Point) arrayList[0];
// Распаковка
Упаковка и распаковка
// 1
var arrayList = new ArrayList();
var p = new Point();
arrayList.Add(p);
// Упаковка
p = (Point) arrayList[0];
// Распаковка
// 2
Int32 x = 5;
Object o = x;
// Упаковка
Int16 y = (Int16) o;
// InvalidCastException
Int16 z = (Int16)(Int32) o; // Распаковка и приведение
Упаковка и распаковка
// 1
var arrayList = new ArrayList();
var p = new Point();
arrayList.Add(p);
// Упаковка
p = (Point) arrayList[0];
// Распаковка
// 2
Int32 x = 5;
Object o = x;
// Упаковка
Int16 y = (Int16) o;
// InvalidCastException
Int16 z = (Int16)(Int32) o; // Распаковка и приведение
// 3
Int32 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 поддерживает конструкторы по умолчанию для

структур
Конструкторы по умолчанию

• .NET поддерживает конструкторы по умолчанию для

структур
• А C# — нет
Конструкторы по умолчанию

• .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()); // 1033533110
Console.WriteLine(a2.GetHashCode()); // 1033533111
var b1 = new KeyValuePair<int, string>(1, "x");
var b2 = new KeyValuePair<int, string>(1, "y");
Console.WriteLine(b1.GetHashCode()); // -1888265882
Console.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);
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/
Спасибо за внимание!

More Related Content

Об особенностях использования значимых типов в .NET

  • 1. Об особенностях использования значимых типов в .NET Андрей Акиньшин Барнаульское сообщество .NET разработчиков bug.ineta.ru www.facebook.com/groups/dotnetbarnaul/
  • 2. Структуры • • • • • Копирование по значению Поддержка boxed и unboxed формы Всегда инициализированы Методы из Equals, GetHashCode из System.ValueType Не могут быть базовыми типами
  • 3. Изменяемые значимые типы 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);
  • 4. Изменяемые значимые типы 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);
  • 5. Изменяемые значимые типы Будут проблемы: // 1. Property public class Circle { public Point Center { get; set; } } // 2. Readonly field public class Circle { public readonly Point Center = new Point(); } // 3. IList var points = new List<Point>();
  • 6. Изменяемые значимые типы 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);
  • 7. Изменяемые значимые типы 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);
  • 8. Упаковка и распаковка // 1 var arrayList = new ArrayList(); var p = new Point(); arrayList.Add(p); // Упаковка p = (Point) arrayList[0]; // Распаковка
  • 9. Упаковка и распаковка // 1 var arrayList = new ArrayList(); var p = new Point(); arrayList.Add(p); // Упаковка p = (Point) arrayList[0]; // Распаковка // 2 Int32 x = 5; Object o = x; // Упаковка Int16 y = (Int16) o; // InvalidCastException Int16 z = (Int16)(Int32) o; // Распаковка и приведение
  • 10. Упаковка и распаковка // 1 var arrayList = new ArrayList(); var p = new Point(); arrayList.Add(p); // Упаковка p = (Point) arrayList[0]; // Распаковка // 2 Int32 x = 5; Object o = x; // Упаковка Int16 y = (Int16) o; // InvalidCastException Int16 z = (Int16)(Int32) o; // Распаковка и приведение // 3 Int32 x = 1; Object y = x; x = 2; Console.WriteLine(x + "/" + (Int32)y);// "2/1"
  • 11. Упаковка и распаковка 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; } }
  • 12. Упаковка и распаковка 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);
  • 13. Конструкторы по умолчанию • .NET поддерживает конструкторы по умолчанию для структур
  • 14. Конструкторы по умолчанию • .NET поддерживает конструкторы по умолчанию для структур • А C# — нет
  • 15. Конструкторы по умолчанию • .NET поддерживает конструкторы по умолчанию для структур • А C# — нет • Но мы всё равно создадим структуру с конструктором по умолчанию, которую назовём MyStruct
  • 16. Конструкторы по умолчанию // Вспомогательные методы 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];
  • 17. GetHashCode() • Быстрая версия (Структура не имеет ссылочных полей, а между её полями нет свободного места) Используем Xor каждых 4 байта структуры • Медленная версия Используем GetHashCode первого нестатичного поля
  • 18. GetHashCode() var a1 = new KeyValuePair<int, int>(1, 2); var a2 = new KeyValuePair<int, int>(1, 3); Console.WriteLine(a1.GetHashCode()); // 1033533110 Console.WriteLine(a2.GetHashCode()); // 1033533111 var b1 = new KeyValuePair<int, string>(1, "x"); var b2 = new KeyValuePair<int, string>(1, "y"); Console.WriteLine(b1.GetHashCode()); // -1888265882 Console.WriteLine(b2.GetHashCode()); // -1888265882
  • 19. 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; }
  • 20. Equals() var redName = Color.Red; var redArgb = Color.FromArgb(255, 255, 0, 0); Console.WriteLine(redName == redArgb);
  • 21. 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; } }
  • 22. Размещение в памяти 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)));
  • 23. Размещение в памяти [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
  • 24. Размещение в памяти 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.
  • 26. Блоги • 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/