Я створюю функцію, де мені потрібно передати об’єкт, щоб він міг бути змінений функцією. Яка різниця між:
public void myFunction(ref MyClass someClass)
і
public void myFunction(out MyClass someClass)
Що я повинен використовувати і чому?
Я створюю функцію, де мені потрібно передати об’єкт, щоб він міг бути змінений функцією. Яка різниця між:
public void myFunction(ref MyClass someClass)
і
public void myFunction(out MyClass someClass)
Що я повинен використовувати і чому?
Відповіді:
ref
повідомляє компілятору, що об'єкт ініціалізується перед входом у функцію, в той час як out
каже компілятору, що об'єкт буде ініціалізований всередині функції.
Тож поки ref
двостороння, out
є лише вихідною.
У ref
модифікатор означає , що:
У out
модифікатор означає , що:
out
, чи можна його читати взагалі в методі, перш ніж він буде встановлений цим методом, якщо він був ініціалізований до виклику методу? Я маю на увазі, чи може метод виклику читати те, що метод виклику передається йому як аргумент?
Скажімо, Дом з'являється в кабінеті Петра про доповідь про звіти TPS.
Якби Дом був аргументом, він мав би надруковану копію записки.
Якби Дом не був аргументом, він змусив би Петра надрукувати нову копію пам’ятки, яку він взяв із собою.
Я спробую спробувати свої пояснення:
Я думаю, ми розуміємо, як працюють цінні типи правильно? Типи значень: (int, long, структура тощо). Коли ви надсилаєте їх до функції без команди ref, вона копіює дані . Все, що ви робите з цими даними у функції, впливає лише на копію, а не на оригінал. Команда ref надсилає дані АКТУАЛЬНІ, і будь-які зміни вплинуть на дані поза функцією.
Підключіть до заплутаної частини посилання:
Дозволяє створити тип посилання:
List<string> someobject = new List<string>()
Коли ви створюєте об'єкт , створюються дві частини:
Тепер , коли ви посилаєте в SomeObject в метод без реф він скопіює опорний покажчик, а НЕ даних. Отже, у вас зараз це:
(outside method) reference1 => someobject
(inside method) reference2 => someobject
Дві посилання, що вказують на один і той же об’єкт. Якщо ви модифікуєте властивість на якомусь об’єкті за допомогою reference2, це вплине на ті самі дані, на які вказує посилання1.
(inside method) reference2.Add("SomeString");
(outside method) reference1[0] == "SomeString" //this is true
Якщо ви скасуєте reference2 або вкажете його на нові дані, це не вплине на reference1, а також на reference11.
(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true
The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject
Тепер то , що відбувається , коли ви посилаєте SomeObject по вих до методу? Фактична посилання на SomeObject відправляється до методу. Отже, у вас є лише одне посилання на дані:
(outside method) reference1 => someobject;
(inside method) reference1 => someobject;
Але що це означає? Він діє точно так само, як надсилати який-небудь об’єкт не за посиланням, за винятком двох головних речей:
1) Якщо ви зведете назовні посилання всередині методу, воно обнулять його поза методом.
(inside method) reference1 = null;
(outside method) reference1 == null; //true
2) Тепер ви можете вказувати посилання на зовсім інше місце розташування даних, а посилання поза функцією тепер буде вказувати на нове місцезнаходження даних.
(inside method) reference1 = new List<string>();
(outside method) reference1.Count == 0; //this is true
ref
та out
параметрами.
out
ключового слова?
Ви повинні використовувати out
перевагу там, де це достатньо для ваших вимог.
У C # метод може повернути лише одне значення. Якщо ви хочете повернути більше одного значення, ви можете використовувати ключове слово out. Вихідний модифікатор повертається як зворотний посилання. Найпростіша відповідь полягає в тому, що ключове слово "out" використовується для отримання значення методу.
У C #, коли ви передаєте тип значення, такий як int, float, double тощо як аргумент параметру методу, воно передається за значенням. Тому, якщо ви змінюєте значення параметра, воно не впливає на аргумент у виклику методу. Але якщо ви позначите параметр ключовим словом «ref», він відобразиться у фактичній змінній.
Розширення собаки, приклад кота. Другий метод із ref змінює об'єкт, на який посилається абонент. Звідси "Кіт" !!!
public static void Foo()
{
MyClass myObject = new MyClass();
myObject.Name = "Dog";
Bar(myObject);
Console.WriteLine(myObject.Name); // Writes "Dog".
Bar(ref myObject);
Console.WriteLine(myObject.Name); // Writes "Cat".
}
public static void Bar(MyClass someObject)
{
MyClass myTempObject = new MyClass();
myTempObject.Name = "Cat";
someObject = myTempObject;
}
public static void Bar(ref MyClass someObject)
{
MyClass myTempObject = new MyClass();
myTempObject.Name = "Cat";
someObject = myTempObject;
}
Оскільки ви передаєте тип посилання (клас), не потрібно використовувати, ref
оскільки за замовчуванням передається лише посилання на фактичний об'єкт, і тому ви завжди змінюєте об'єкт за посиланням.
Приклад:
public void Foo()
{
MyClass myObject = new MyClass();
myObject.Name = "Dog";
Bar(myObject);
Console.WriteLine(myObject.Name); // Writes "Cat".
}
public void Bar(MyClass someObject)
{
someObject.Name = "Cat";
}
Доки ви переходите в клас, вам не доведеться користуватися, ref
якщо ви хочете змінити об'єкт всередині свого методу.
someObject = null
to Bar
end Execute. Ваш код буде спрацьовувати нормально, оскільки скасовується лише Bar
посилання на примірник. Тепер перейдіть Bar
на Bar(ref MyClass someObject)
і виконайте знову - ви отримаєте, NullReferenceException
оскільки Foo
посилання на екземпляр теж було анульовано.
ref
і out
поводяться аналогічно, за винятком наступних відмінностей.
ref
змінна повинна бути ініціалізована перед використанням. out
змінна може використовуватися без призначенняout
Параметр повинен розглядатися як непризначене значення функцією, яка його використовує. Отже, ми можемо використовувати ініціалізований out
параметр у коді виклику, але значення буде втрачено при виконанні функції.Для тих, хто вчиться на прикладі (як я), ось що говорить Ентоні Колесов .
Я створив кілька мінімальних прикладів реф, поза та інших, щоб проілюструвати суть. Я не висвітлюю найкращих практик, лише приклади, щоб зрозуміти відмінності.
"Бейкер"
Це тому, що перший змінює ваш рядковий посилання на вказівку "Бейкер". Змінення посилання можливо, оскільки ви передали його за допомогою ключового слова ref (=> посилання на посилання на рядок). Другий виклик отримує копію посилання на рядок.
рядок виглядає спочатку якось особливим. Але рядок - це лише еталонний клас, і якщо ви визначаєте
string s = "Able";
тоді s - посилання на рядок класу, який містить текст "Able"! Ще одне призначення тій самій змінній через
s = "Baker";
не змінює початковий рядок, а просто створює новий екземпляр і нехай вказує s на цей екземпляр!
Ви можете спробувати з наступним невеликим прикладом коду:
string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);
Що ти очікуєш? Що ви отримаєте, все ще є "Здатним", оскільки ви просто встановите посилання в s на інший екземпляр, а s2 вказує на початковий екземпляр.
EDIT: рядок також непорушний, що означає, що просто немає методу чи властивості, що модифікує існуючий екземпляр рядка (ви можете спробувати знайти його в документах, але ви не будете довершені :-)). Усі способи маніпулювання рядками повертають новий екземпляр рядка! (Ось чому ви часто отримуєте кращі показники, використовуючи клас StringBuilder)
ref означає, що значення в параметрі ref вже встановлено, метод може його читати та змінювати. Використання ключового слова ref - це те саме, що сказати, що абонент відповідає за ініціалізацію значення параметра.
out повідомляє компілятору, що ініціалізація об'єкта є відповідальністю функції, функція повинна призначити вихідний параметр. Заборонено залишати це без призначення.
Вихід: Оператор повернення може використовуватися для повернення лише одного значення функції. Однак, використовуючи вихідні параметри, ви можете повернути два значення з функції. Вихідні параметри схожі на опорні параметри, за винятком того, що вони передають дані з методу, а не в них.
Наступний приклад ілюструє це:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void getValue(out int x )
{
int temp = 5;
x = temp;
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* local variable definition */
int a = 100;
Console.WriteLine("Before method call, value of a : {0}", a);
/* calling a function to get the value */
n.getValue(out a);
Console.WriteLine("After method call, value of a : {0}", a);
Console.ReadLine();
}
}
}
ref: Довідковий параметр - це посилання на місце пам'яті змінної. Коли ви передаєте параметри за посиланням, на відміну від значущих параметрів, для цих параметрів не створюється нове місце зберігання. Референтні параметри представляють те саме місце пам'яті, що і фактичні параметри, що надходять до методу.
У C # ви оголошуєте опорні параметри за допомогою ключового слова ref. Наступний приклад демонструє це:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* save the value of x */
x = y; /* put y into x */
y = temp; /* put temp into y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* local variable definition */
int a = 100;
int b = 200;
Console.WriteLine("Before swap, value of a : {0}", a);
Console.WriteLine("Before swap, value of b : {0}", b);
/* calling a function to swap the values */
n.swap(ref a, ref b);
Console.WriteLine("After swap, value of a : {0}", a);
Console.WriteLine("After swap, value of b : {0}", b);
Console.ReadLine();
}
}
}
ref і out працюють так само, як проходження посилання та проходження вказівниками, як у C ++.
Для посилання аргумент повинен оголосити та ініціалізувати.
Для, аргумент повинен оголошуватися, але може бути, а може ініціалізуватися
double nbr = 6; // if not initialized we get error
double dd = doit.square(ref nbr);
double Half_nbr ; // fine as passed by out, but inside the calling method you initialize it
doit.math_routines(nbr, out Half_nbr);
out double Half_nbr
.
Час авторства:
(1) Ми створюємо метод виклику Main()
(2) він створює об'єкт List (який є об'єктом еталонного типу) і зберігає його у змінній myList
.
public sealed class Program
{
public static Main()
{
List<int> myList = new List<int>();
Під час виконання:
(3) Виконання часу виділяє пам'ять у стеці на # 00, достатньо широку, щоб зберігати адресу (# 00 = myList
, оскільки імена змінних дійсно просто псевдоніми для місць розташування пам'яті)
(4) Під час виконання створюється об’єкт списку в купі в пам'яті #FF (усі ці адреси, наприклад, збережені)
(5) Потім час виконання зберігатиме початкову адресу #FF об'єкта під № 00 (або словами, зберігає вказівник об'єкта List у вказівнику myList
)
Повернутися до часу створення:
(6) Потім ми передаємо об'єкт List як аргумент myParamList
викликаному методу modifyMyList
і присвоюємо йому новий об'єкт List
List<int> myList = new List<int>();
List<int> newList = ModifyMyList(myList)
public List<int> ModifyMyList(List<int> myParamList){
myParamList = new List<int>();
return myParamList;
}
Під час виконання:
(7) Виконання часу запускає процедуру виклику для виклику методу і як частина його перевіряє тип параметрів.
(8) Після знаходження опорного типу, він виділяє пам'ять на стек №04 для випромінення змінної параметра myParamList
.
(9) Потім він також зберігає значення #FF у ньому.
(10) Виконання часу створює об'єкт списку в купі в пам’яті № 004 і замінює #FF в # 04 цим значенням (або відміняє оригінальний об’єкт List і вказує на новий об’єкт List у цьому методі)
Адреса в # 00 не змінена і зберігає посилання на #FF (або вихідний myList
покажчик не порушений).
Посилань Ключове слово є директива компілятора , щоб пропустити генерацію коду під час виконання для (8) і (9) , що означає , що не буде ніякого розподілу купи для параметрів методу. Він буде використовувати оригінальний покажчик №00 для роботи над об'єктом на #FF. Якщо оригінальний покажчик не ініціалізований, час виконання зупиняється, скарга не може продовжуватися, оскільки змінна не ініціалізується
З ключових слів є директива компілятора , який в значній мірі так само , як вих з невеликою зміною в (9) і (10). Компілятор очікує, що аргумент буде неініціалізованим і продовжить (8), (4) та (5) створити об’єкт у купі та зберегти його вихідну адресу у змінній аргументу. Жодна неініціалізована помилка не буде викинута, і будь-яка попередня збережена посилання буде втрачена.
Окрім того, що ви можете перепризначити чужу змінну в інший екземпляр класу, повернути кілька значень тощо, використовуючи ref
або out
даючи комусь іншому знати, що вам потрібно від них, і що ви маєте намір робити зі змінною, яку вони надають
Вам не потрібно ref
, або out
якщо всі , що ви збираєтеся зробити , це змінити речі всередині в MyClass
разі , який приймає в якості аргументу someClass
.
someClass.Message = "Hello World"
ви користуєтесь ref
, out
чи нічогоsomeClass = new MyClass()
всередину myFunction(someClass)
витісняє об'єкт, що бачиться лише someClass
в межах myFunction
методу. Метод виклику все ще знає про створений ним оригінальний MyClass
екземпляр і передається вашому методуВам потрібно ref
або out
якщо ви плануєте замінити someClass
вихід на абсолютно новий об'єкт і хочете, щоб метод виклику бачив зміни
someClass = new MyClass()
всередині myFunction(out someClass)
змінює об'єкт, який бачить метод, який викликавmyFunction
І вони хочуть знати, що ви збираєтеся робити зі своїми даними. Уявіть, що ви пишете бібліотеку, яку використовуватимуть мільйони розробників. Ви хочете, щоб вони знали, що ви збираєтеся робити зі своїми змінними, коли вони викликають ваші методи
Використовуючи ref
робить вислів "Передати змінну, присвоєну якомусь значенню, коли ви викликаєте мій метод. Будьте в курсі, що я міг би змінити його на щось інше цілком під час мого методу. Не сподівайтеся, що ваша змінна буде вказувати на старий об'єкт коли я закінчу "
Використовуючи out
робить вислів "Передати змінну заповнювача заповнення моєму методу. Неважливо, має він значення чи ні; компілятор змусить мене призначити його новим значенням. Я абсолютно гарантую, що об'єкт, на який вказує ваш змінна, перш ніж ви викликали мій метод, буде відрізнятися від часу, коли я закінчу
in
теж є модифікаторІ це заважає методу замінювати переданий екземпляр на інший екземпляр. Подумайте про це, як сказати тим мільйонам розробників: "передайте мені свою оригінальну посилання на змінну, і я обіцяю не замінювати ваші ретельно складені дані на щось інше". in
має деякі особливості, а в деяких випадках, наприклад, коли може знадобитися неявна конверсія, щоб зробити ваш короткий сумісний із in int
компілятором, тимчасово зробить int, розширить ваш короткий до нього, передасть посилання та закінчить. Це можна зробити, тому що ви заявили, що не збираєтеся з цим возитися.
Microsoft зробила це .TryParse
методами для числових типів:
int i = 98234957;
bool success = int.TryParse("123", out i);
Позначаючи параметр, коли out
вони активно декларують тут, "ми обов'язково змінимо ваше кропітко продумане значення 98234957 на щось інше"
Звичайно, їм щось доводиться робити для таких речей, як розбір типів значень, тому що якби метод розбору не дозволив поміняти тип значення на щось інше, він би не дуже добре працював. Але уявіть, що в деяких був вигаданий метод бібліотеку, яку ви створюєте:
public void PoorlyNamedMethod(out SomeClass x)
Ви можете бачити, що це out
, і, таким чином, ви можете знати, що якщо ви витрачаєте години на стискання чисел, створюючи ідеальний SomeClass:
SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);
Ну, це було марною витратою часу, зайнявши всі ці години, щоб зробити цей ідеальний клас. Це, безумовно, буде викинуто і замінено PoorlyNamedMethod
Для тих, хто шукає лаконічну відповідь.
І ключові слова,
ref
іout
ключові слова використовуються для проходження повзreference
.
Змінна
ref
ключового слова повинна мати значення або повинна посилатися на об'єкт абоnull
до його передачі.
На відміну від цього
ref
, зміннаout
ключового слова повинна мати значення або повинна посилатися на об’єкт абоnull
після його передачі, а також не потрібно мати значення або посилатися на об'єкт перед передачею.
Щоб проілюструвати безліч чудових пояснень, я розробив таку консольну програму:
using System;
using System.Collections.Generic;
namespace CSharpDemos
{
class Program
{
static void Main(string[] args)
{
List<string> StringList = new List<string> { "Hello" };
List<string> StringListRef = new List<string> { "Hallo" };
AppendWorld(StringList);
Console.WriteLine(StringList[0] + StringList[1]);
HalloWelt(ref StringListRef);
Console.WriteLine(StringListRef[0] + StringListRef[1]);
CiaoMondo(out List<string> StringListOut);
Console.WriteLine(StringListOut[0] + StringListOut[1]);
}
static void AppendWorld(List<string> LiStri)
{
LiStri.Add(" World!");
LiStri = new List<string> { "¡Hola", " Mundo!" };
Console.WriteLine(LiStri[0] + LiStri[1]);
}
static void HalloWelt(ref List<string> LiStriRef)
{ LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }
static void CiaoMondo(out List<string> LiStriOut)
{ LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
}
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
AppendWorld
: Передається копія StringList
імені LiStri
. На початку методу ця копія посилається на оригінальний список і тому може бути використана для зміни цього списку. Пізніше LiStri
посилається на інший List<string>
об’єкт всередині методу, який не впливає на вихідний список.
HalloWelt
: LiStriRef
- псевдонім вже ініціалізованого
ListStringRef
. Переданий List<string>
об'єкт використовується для ініціалізації нового, тому ref
був необхідним.
CiaoMondo
: LiStriOut
є псевдонімом ListStringOut
і має бути ініціалізовано.
Отже, якщо метод просто модифікує об'єкт, на який посилається передана змінна, компілятор не дозволить вам використовувати, out
і ви не повинні використовувати, ref
оскільки це заплутає не компілятор, а читач коду. Якщо метод зробить переданий аргумент посиланням іншого об'єкта, використовуйте ref
для вже ініціалізованого об'єкта та out
для методів, які повинні ініціалізувати новий об'єкт для переданого аргументу. Крім того, ref
і out
поводитись так само.
Вони майже однакові - різниця лише в тому, що змінну, яку ви передаєте як параметр out, не потрібно ініціалізувати, а метод, що використовує параметр ref, повинен щось встановити.
int x; Foo(out x); // OK
int y; Foo(ref y); // Error
Параметри відбиття призначені для даних, які можуть бути змінені, вихідні параметри - це дані, які є додатковим висновком для функції (наприклад, int.TryParse), які вже використовують щось повернене значення.
Нижче я показав приклад, використовуючи і Ref, і Out . Тепер ви все будете звільнені від посилання та виходу.
У наведеному нижче прикладі, коли я коментую // myRefObj = new myClass {Name = "ref за межами дзвонив !!"}; рядок, отримає помилку із записом "Використання непризначеної локальної змінної" myRefObj "" , але такої помилки у виході немає .
Де використовувати Ref : коли ми викликаємо процедуру з параметром in і той самий параметр буде використаний для зберігання результатів цього процесу.
Де використовувати Out: коли ми викликаємо процедуру без параметра в параметрі, і той самий парам буде використаний для повернення значення з цього proc. Також відзначте результат
public partial class refAndOutUse : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
myClass myRefObj;
myRefObj = new myClass { Name = "ref outside called!! <br/>" };
myRefFunction(ref myRefObj);
Response.Write(myRefObj.Name); //ref inside function
myClass myOutObj;
myOutFunction(out myOutObj);
Response.Write(myOutObj.Name); //out inside function
}
void myRefFunction(ref myClass refObj)
{
refObj.Name = "ref inside function <br/>";
Response.Write(refObj.Name); //ref inside function
}
void myOutFunction(out myClass outObj)
{
outObj = new myClass { Name = "out inside function <br/>" };
Response.Write(outObj.Name); //out inside function
}
}
public class myClass
{
public string Name { get; set; }
}
public static void Main(string[] args)
{
//int a=10;
//change(ref a);
//Console.WriteLine(a);
// Console.Read();
int b;
change2(out b);
Console.WriteLine(b);
Console.Read();
}
// static void change(ref int a)
//{
// a = 20;
//}
static void change2(out int b)
{
b = 20;
}
ви можете перевірити цей код, він опише вам його повну різницю, коли ви використовуєте "ref" його означає, що ви вже ініціалізуєте цей int / string
але коли ви використовуєте "out", він працює в обох умовах, коли ви ініціалізуєте цей int / string чи ні, але ви повинні ініціалізувати цей int / string у цій функції
Ref: Ключове слово ref використовується для передачі аргументу як посилання. Це означає, що коли значення цього параметра змінюється в методі, воно відображається в методі виклику. Аргумент, який передається за допомогою ключового слова ref, повинен бути ініціалізований у методі виклику до того, як він буде переданий викличеному методу.
Вихід: Ключове слово out також використовується для передачі такого аргументу, як ключове слово ref, але аргумент можна передавати, не присвоюючи йому жодного значення. Аргумент, переданий за допомогою ключового слова out, повинен бути ініціалізований у виклику методу, перш ніж він повернеться до методу виклику.
public class Example
{
public static void Main()
{
int val1 = 0; //must be initialized
int val2; //optional
Example1(ref val1);
Console.WriteLine(val1);
Example2(out val2);
Console.WriteLine(val2);
}
static void Example1(ref int value)
{
value = 1;
}
static void Example2(out int value)
{
value = 2;
}
}
/* Output 1 2
Перегляньте і перевантажуйте метод перевантаження
І перегляд, і вихід не можна використовувати одночасно в перевантаженні методу. Тим не менш, ref і out трактуються по-різному під час виконання, але вони обробляються однаково під час компіляції (CLR не розмежовує два, в той час як він створював IL для ref та out).
З точки зору методу, який отримує параметр, різниця між ref
і out
полягає в тому, що C # вимагає, щоб методи повинні записувати кожен out
параметр перед поверненням, і не повинні робити нічого з таким параметром, крім передачі його як out
параметра або запису до нього , поки він не буде переданий як out
параметр іншому методу або записаний безпосередньо. Зауважте, що деякі інші мови не пред'являють таких вимог; віртуальний або інтерфейсний метод, який оголошується в C # за допомогоюout
параметром, може бути замінений іншою мовою, яка не накладає жодних спеціальних обмежень на такі параметри.
З точки зору абонента, C # за багатьох обставин припускає, що коли виклик методу з out
параметром призведе до того, що передана змінна буде записана, не прочитавши її спочатку. Це припущення може бути невірним при виклику методів, написаних іншими мовами. Наприклад:
struct MyStruct
{
...
myStruct(IDictionary<int, MyStruct> d)
{
d.TryGetValue(23, out this);
}
}
Якщо myDictionary
ідентифікується IDictionary<TKey,TValue>
реалізація, написана мовою, відмінною від C #, хоч це MyStruct s = new MyStruct(myDictionary);
виглядає як призначення, воно потенційно може залишитись s
без змін.
Зауважте, що конструктори, написані на VB.NET, на відміну від C #, не роблять припущень щодо того, чи будуть виправлені методи змінювати будь-які out
параметри, і очищати всі поля беззастережно. Незвичайна поведінка, на яку згадувалося вище, не відбуватиметься з кодом, записаним повністю в VB або повністю в C #, але може виникнути, коли код, написаний на C #, викликає метод, написаний у VB.NET.
Якщо ви хочете передати свій параметр як посилання, вам слід ініціалізувати його, перш ніж передати параметр функції інший компілятор, сам покаже помилку. Але в разі виходу параметра вам не потрібно ініціалізувати об'єктний параметр, перш ніж передавати його в метод. Ви можете ініціалізувати об'єкт у самому методі виклику.
Майте на увазі, що опорний параметр, переданий всередині функції, безпосередньо працює.
Наприклад,
public class MyClass
{
public string Name { get; set; }
}
public void Foo()
{
MyClass myObject = new MyClass();
myObject.Name = "Dog";
Bar(myObject);
Console.WriteLine(myObject.Name); // Writes "Dog".
}
public void Bar(MyClass someObject)
{
MyClass myTempObject = new MyClass();
myTempObject.Name = "Cat";
someObject = myTempObject;
}
Це напише Собака, а не Кіт. Отже, вам слід безпосередньо працювати над деякимОб'єктом.
Я, можливо, не дуже хороший у цьому, але, безумовно, рядки (навіть якщо вони технічно довідкові типи і живуть на купі) передаються за значенням, а не посиланням?
string a = "Hello";
string b = "goodbye";
b = a; //attempt to make b point to a, won't work.
a = "testing";
Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!
Ось чому вам потрібна посилання, якщо ви хочете, щоб зміни існували поза межами функції, яка їх створює, ви не передаєте посилання в іншому випадку.
Наскільки я знаю, вам потрібен лише ref для типів структур / значень і самої рядки, оскільки рядок є еталонним типом, який робить вигляд, але не є типом значення.
Я міг би тут абсолютно помилитися, я новачок.
Capitalize()
який змінює зміст рядка на великі літери. Якщо ви замінили рядок a = "testing";
на a.Capitalize();
, то ваш вихід буде "HELLO", а не "Hello". Однією з переваг непорушних типів є те, що ви можете обходити посилання та не турбуватися про зміну іншого значення коду.
MyClass
це був биclass
тип, тобто тип посилання. У такому випадку об'єкт, який ви передаєте, може бути змінений заmyFunction
допомогою ключового слова evenref
/ no /out
.myFunction
отримає нове посилання, яке вказує на один і той же об'єкт, і він може змінювати цей самий об'єкт скільки завгодно. Різниця, якуref
зробило б ключове слово, полягала б у тому, щоmyFunction
отримали те саме посилання на той самий об’єкт. Це було б важливо лише, якбиmyFunction
змінити посилання, щоб вказати на інший об'єкт.