استفاده از Ref Structs در #C برای بهبود عملکرد
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۱/۲۰ ۰۸:۵۱
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
public ref struct RefPoint
{
public ref int X;
public ref int Y;
public RefPoint(ref int x, ref int y)
{
X = ref x;
Y = ref y;
}
}Point یک ساختار معمولی است که مقادیر را کپی میکند، در حالی که RefPoint از ارجاعات استفاده میکند و به دادههای اصلی اشاره دارد. حالا بیایید نحوه استفاده از آنها را ببینیم:static void Main()
{
int x = 10, y = 20;
// استفاده از Struct معمولی
Point p1 = new Point(x, y);
p1.X = 15; // این تغییر فقط در کپی اعمال میشود
Console.WriteLine($"x: {x}, p1.X: {p1.X}"); // خروجی: x: 10, p1.X: 15
// استفاده از Ref Struct
RefPoint p2 = new RefPoint(ref x, ref y);
p2.X = 25; // این تغییر مستقیماً روی x اعمال میشود
Console.WriteLine($"x: {x}, p2.X: {p2.X}"); // خروجی: x: 25, p2.X: 25
}RefPoint بهصورت مستقیم به متغیرهای اصلی ارجاع میدهد و تغییر آنها را در منبع منعکس میکند، در حالی که Point یک کپی مستقل ایجاد میکند.Span<T>Span<T> است، که خود یک ref struct است و برای دسترسی مستقیم به حافظه استفاده میشود. فرض کنید میخواهیم مجموع عناصر یک آرایه را محاسبه کنیم:static int SumArray(int[] numbers)
{
int sum = 0;
foreach (var num in numbers)
{
sum += num;
}
return sum;
}
static int SumSpan(Span<int> numbers)
{
int sum = 0;
for (int i = 0; i < numbers.Length; i++)
{
sum += numbers[i];
}
return sum;
}SumSpan از Span<int> استفاده میکند که بهصورت مستقیم به حافظه آرایه دسترسی دارد و نیازی به کپی کردن دادهها ندارد. برای مقایسه عملکرد، میتوانیم یک تست ساده بنویسیم:static void Main()
{
int[] numbers = Enumerable.Range(0, 1000000).ToArray();
var watch = Stopwatch.StartNew();
int sum1 = SumArray(numbers);
watch.Stop();
Console.WriteLine($"SumArray: {sum1}, Time: {watch.ElapsedMilliseconds} ms");
watch = Stopwatch.StartNew();
int sum2 = SumSpan(numbers.AsSpan());
watch.Stop();
Console.WriteLine($"SumSpan: {sum2}, Time: {watch.ElapsedMilliseconds} ms");
}SumSpan به دلیل دسترسی مستقیم و عدم نیاز به کپی، سریعتر عمل میکند، بهویژه در آرایههای بزرگ.public ref struct NumberManipulator
{
private Span<int> _data;
public NumberManipulator(Span<int> data)
{
_data = data;
}
public ref int GetElement(int index) => ref _data[index];
public void DoubleElement(in int index)
{
_data[index] *= 2;
}
}
static void Main()
{
int[] numbers = [1, 2, 3, 4];
var manipulator = new NumberManipulator(numbers.AsSpan());
ref int element = ref manipulator.GetElement(1);
element = 10; // تغییر مستقیم در آرایه
manipulator.DoubleElement(2); // ضربدر 2 کردن عنصر سوم
Console.WriteLine(string.Join(", ", numbers)); // خروجی: 1, 10, 6, 4
}GetElement یک ارجاع به عنصر آرایه برمیگرداند و DoubleElement با استفاده از پارامتر in از کپی غیرضروری جلوگیری میکند.Span<T> و Memory<T> در کنار ref structs بهره ببرید تا حداکثر کارایی را به دست آورید.Span<T>، ref structs نشان دادهاند که میتوانند کارایی را بهطور قابلتوجهی افزایش دهند.