Отговори на тема  [ 7 мнения ] 
Предаване на параметър на метод по стойност в C# 
Автор Съобщение
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Пет Фев 25, 2005 1:58 pm
Мнения: 4585
Местоположение: US
Мнение Предаване на параметър на метод по стойност в C#
C# по подразбиране предава параметрите на методите по адрес (reference), изключения са
базовите типове данни (int, double, string и т.н.), както и прости структури (които нямат
масиви например). Имам следната C# структура:
Код:
public struct TPCANMsg
{
    public uint ID;
    [MarshalAs(UnmanagedType.U1)]
    public TPCANMessageType MSGTYPE; 
    public byte LEN;     
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] DATA;   
}

Тази структура ще се добавя от работна нишка в стандартна System.Collections.Generic.Queue,
след което сигнализира главната нишка за последваща обработка.
Направих нещо съвсем просто: заделих памет за TPCANMsg в работната нишка и всеки път когато
дойдат данни ги копирам в нейните полета, след което викам Queue.Enqueue(msg). Понеже DATA[] е масив,
C# го предава на Enqueue() като адрес и съответно в опашката всички предходни елементи
се променят, което не искам да бъде така.

Целият код е по-долу:
Код:
public void PrintPCANMsg(TPCANMsg msg)
{
    string szHex = Convert.ToString(msg.ID, 16);
    szHex = szHex.ToUpper();
    Console.WriteLine("ID: 0x{0}; LEN: {1}; DATA[]:" +
        " 0x{2:X}, 0x{3:X}, 0x{4:X}, 0x{5:X}, 0x{6:X}, 0x{7:X}, 0x{8:X}, 0x{9:X}",
        szHex, msg.LEN, msg.DATA[0], msg.DATA[1], msg.DATA[2], msg.DATA[3], msg.DATA[4], msg.DATA[5], msg.DATA[6], msg.DATA[7]);
}

public void QueueTest()
{
    Queue<TPCANMsg> queue = new Queue<TPCANMsg>();
    TPCANMsg msg = new TPCANMsg();
    msg.DATA = new byte[8];

    Console.WriteLine("Enqueue() method: ");

    msg.LEN = 8;
    msg.MSGTYPE = TPCANMessageType.PCAN_MESSAGE_STANDARD;
   
    /// Enqueue 1st msg
    ///
    msg.DATA[0] = 0x11; msg.DATA[1] = 0x12; msg.DATA[2] = 0x13; msg.DATA[3] = 0x14;
    msg.DATA[4] = 0x15; msg.DATA[5] = 0x16; msg.DATA[6] = 0x17; msg.DATA[7] = 0x18;
    msg.ID = 0x18FFD61E;

    queue.Enqueue(msg);
    PrintPCANMsg(msg);

    /// Enqueue 2nd msg
    ///
    msg.DATA[0] = 0x21; msg.DATA[1] = 0x22; msg.DATA[2] = 0x23; msg.DATA[3] = 0x24;
    msg.DATA[4] = 0x25; msg.DATA[5] = 0x26; msg.DATA[6] = 0x27; msg.DATA[7] = 0x28;
    msg.ID = 0x18FFE01E;
    queue.Enqueue(msg);
    PrintPCANMsg(msg);

    /// Dequeue and Print all messages
    ///
    Console.WriteLine("Dequeue() method: ");
    while ( queue.Count > 0 )
    {
        msg = queue.Dequeue();
        PrintPCANMsg(msg);
    }
}


Резултата от кода:
Код:
Enqueue() method:
ID: 0x18FFD61E; LEN: 8; DATA[]: 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
ID: 0x18FFE01E; LEN: 8; DATA[]: 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28
Dequeue() method:
ID: 0x18FFD61E; LEN: 8; DATA[]: 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28
ID: 0x18FFE01E; LEN: 8; DATA[]: 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28


Както се вижда от резултата C# е подал DATA[] масива като адрес, затова
изваждайки двата елемента имам еднакви DATA[], докато ID е наред.

Най-тъпото което се сетих е да променя структурата така:
Код:
public struct MYCANMsg
{
    public uint ID;
    public TPCANMessageType MSGTYPE;
    public byte LEN;
    public byte D0;
    public byte D1;
    public byte D2;
    public byte D3;
    public byte D4;
    public byte D5;
    public byte D6;
    public byte D7;
}

След което при Enqueue() и Dequeue() с опашката всичко е наред, понеже този път
в структурата няма масив и C# я предава като параметър по стойност.

Не ми се ще обаче да имам такава междинна структура, искам да е оригиналната с масив
в нея. Има ли начин на C# да се укаже да предаде структура/клас като параметър на метод
по стойност ? Ако няма, как постъпвате когато трябва да предадете по стойност ?

_________________
Ето аз дишам, работя, живея и програми пиша тъй както умея, с проца под вежди се гледаме строго и боря се с него доколкото мога....


Пет Окт 15, 2021 6:07 pm
Профил WWW
Ранг: Форумен бог
Ранг: Форумен бог

Регистриран на: Нед Яну 01, 2012 8:04 pm
Мнения: 2663
Местоположение: София / Велико Търново
Мнение Re: Предаване на параметър на метод по стойност в C#
Ами видимо имаш само един физически създаден масив msg.DATA = new byte[8]; при Dequeue() пак дърпаш "неговата инстанция", фактически нашиваш данните два пъти на едно място и при Dequeue() дърпаш последните данни.

....

Според мен трябва на второто зареждане на данни за тест, да напишеш още веднъж msg.DATA = new byte[8]; и да инициализираш новите данни. Така ще имаш 2 физически масива в паметта които да държат данните.

edit: Ако данните за масива идват от масив, може да използваш Array.Copy да не ги зареждаш с цикъл елемент по елемент. То този метод сигурно така работи де...

_________________
https://github.com/slav4ocom/


Пет Окт 15, 2021 7:43 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог

Регистриран на: Вто Окт 11, 2011 11:53 pm
Мнения: 4582
Местоположение: Brussels / Пловдив
Мнение Re: Предаване на параметър на метод по стойност в C#
Трябва да си направиш shallow или deep clone на обекта - пусни едни гугле и ще намериш достатъчно страници как да подкараш всичко.

_________________
Мразя да мразя ...


Нед Окт 17, 2021 6:36 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Пет Фев 25, 2005 1:58 pm
Мнения: 4585
Местоположение: US
Мнение Re: Предаване на параметър на метод по стойност в C#
Благодаря за отговорите.

Всъщност бях разбрал, че му липсва
Код:
new byte [8]
,
но по скоро ме интересуваше някакъв друг начин. Ще видя тези
shallow и deep clone, но видях че C# предлага и още един начин, fixed:
Код:
public struct CANMsg
{
    public uint ID;
    public TPCANMessageType MSGTYPE;
    public byte LEN;
    public fixed byte DATA[8];
}


но при компилиране дава грешка:

Код:
Severity   Code   Description   Project   File   Line   Suppression State
Error   CS0214   Pointers and fixed size buffers may only be used in an unsafe context   QueueExample3   C:\MyFile\Projects\CSharp\QueueExample3\CANQueue.cs   18   Active


Добавям unsafe към дефиницията на структурата:
Код:
public unsafe struct CANMsg
{
    public uint ID;
    public TPCANMessageType MSGTYPE;
    public byte LEN;
    public fixed byte DATA[8];
}


и сега се оплаква, че:

Код:
Severity   Code   Description   Project   File   Line   Suppression State
Error   CS0227   Unsafe code may only appear if compiling with /unsafe   QueueExample3   C:\MyFile\Projects\CSharp\QueueExample3\CANQueue.cs   13   Active


Та сега въпроса ми е: fixed и unsafe за структурата, компилирам проекта като unsafe и работи под Windows,
но това unsafe/fixed дали няма да създаде подводни камъни при билдване на други платформи ?

C# е малко като манджа с грозде :D

_________________
Ето аз дишам, работя, живея и програми пиша тъй както умея, с проца под вежди се гледаме строго и боря се с него доколкото мога....


Пон Окт 18, 2021 4:32 pm
Профил WWW
Ранг: Форумен бог
Ранг: Форумен бог

Регистриран на: Нед Яну 01, 2012 8:04 pm
Мнения: 2663
Местоположение: София / Велико Търново
Мнение Re: Предаване на параметър на метод по стойност в C#
Ами когато включиш unsafe опцията вече може и така да стане :)

Доколкото виждам struct във C# еквивалентно на typedef struct във C/C++. T.e. това не е обект а сложен тип. Т.е. трябва да напишеш нещо от сорта на myStruct CANMsg; naBaiIvanStructurata CANMsg;
Всъщност защо не използваш само класове ?

Принципно всеки метод получава аргументите по стойност. Референтните типове са пак стойност която държи тип и адрес на обекта към който сочат.

За да може реално Dequeue() да извлече данните обекта който се подава на Queue() трябва да съществува в паметта във момента на Dequeue() така той реално ще съхранява физически въпросният масив, а в опашката ще имаш референция към него. Ако обектът който се подава на Queue() е Dispose()-нат към момента на Dequeue() естествено трябва да си правиш локални копия в твоите обекти от опашката. :)

_________________
https://github.com/slav4ocom/


Пон Окт 18, 2021 8:51 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Пет Фев 25, 2005 1:58 pm
Мнения: 4585
Местоположение: US
Мнение Re: Предаване на параметър на метод по стойност в C#
Не ползвам само класове, защото чета от C++ DLL, и там данните са struct.

_________________
Ето аз дишам, работя, живея и програми пиша тъй както умея, с проца под вежди се гледаме строго и боря се с него доколкото мога....


Вто Окт 19, 2021 2:03 pm
Профил WWW
Ранг: Почетен член
Ранг: Почетен член
Аватар

Регистриран на: Съб Сеп 17, 2005 5:07 pm
Мнения: 813
Местоположение: Сливен
Мнение Re: Предаване на параметър на метод по стойност в C#
Добре си се сетил за unsafe .. по принцип щом ти ще си менаджираш паметта с подобни гимнастики трябва да го кажеш на билдера. Отиваш на Properties на проекта и цъкаш отметката AllowUnsafeCode от Build менюто


Прикачени файлове:
Untitled.jpg
Untitled.jpg [ 256.83 KiB | Прегледано 2153 пъти ]

_________________
Код:
while(true);
Сря Окт 20, 2021 10:24 pm
Профил ICQ
Покажи мненията от миналия:  Сортирай по  
Отговори на тема   [ 7 мнения ] 

Кой е на линия

Потребители разглеждащи този форум: 0 регистрирани и 0 госта


Вие не можете да пускате нови теми
Вие не можете да отговаряте на теми
Вие не можете да променяте собственото си мнение
Вие не можете да изтривате собствените си мнения
Вие не можете да прикачвате файл

Търсене:
Иди на:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.
Хостинг и Домейни