引用類型
引用類型的變量存儲對其數(shù)據(jù)(對象)的引用,而值類型的變量直接包含其數(shù)據(jù)。 對于引用類型,兩種變量可引用同一對象;因此,對一個變量執(zhí)行的操作會影響另一個變量所引用的對象。 對于值類型,每個變量都具有其自己的數(shù)據(jù)副本,對一個變量執(zhí)行的操作不會影響另一個變量(in、ref 和 out 參數(shù)變量除外)。
在前文中,我們常常說引用類型比值類型要好,一方面引用類型的對象類似于指針,不同的變量會指向同一個對象。而值類型則是將定義的值克隆一個副本賦值給新的變量。從性能方面引用類型就更好,并且從內(nèi)存方面考慮,引用類型存放在CLR的虛擬機堆中,而值類型存放在棧中,在堆中可以對數(shù)據(jù)直接處理,而在棧中則需要將其前面的內(nèi)存出棧,從內(nèi)存方面使用引用類型也更好。而具體的關(guān)于引用和值類型在內(nèi)存中的關(guān)系,將在未來GC的章節(jié)中描述。
class
類應該是最熟悉的一種引用類型了
class ClassA { } --直接定義類
class DerivedClass : BaseClass { } --繼承單個類
class ImplClass : IFace1, IFace2 { } -- 實現(xiàn)兩個接口
class ImplDerivedClass : BaseClass, IFace1 { } --繼承一個類,實現(xiàn)一個接口
在C#中,我們只能繼承一個類,但是可以繼承多個接口。(看到本文,我默認你學過面向?qū)ο罅耍?/p>
匿名類
匿名類型提供了一種方便的方法,可用來將一組只讀屬性封裝到單個對象中,而無需首先顯式定義一個類型。 類型名由編譯器生成,并且不能在源代碼級使用。 每個屬性的類型由編譯器推斷。
var v = new { Amount = 108, Message = "Hello" };
// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and string.
Console.WriteLine(v.Amount + v.Message); //108Hello
使用匿名類,我們定義新的對象的時候可以不必定義一個類,這是為了方便我們進行一些對象的生成,特別是當這個類是臨時定義的,只需要屬性而不需要其他方法,且有可能只會定義一次對象的時候。
匿名類型包含一個或多個公共只讀屬性。 包含其他種類的類成員(如方法或事件)為無效。 用來初始化屬性的表達式不能為 null、匿名函數(shù)或指針類型。
在下例中,我們用prod遍歷了products集合,并在每次創(chuàng)建一個新的匿名類,其中屬性為Color和Pirce
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
盡管我們并沒有給匿名類中的屬性創(chuàng)建變量名,但是匿名類自動的將創(chuàng)建時的變量名作為了自己的變量名,也就是創(chuàng)建時使用了prod.Color,prod.Price
。它根據(jù)變量名為自己的屬性也創(chuàng)建了相應的同名變量Color
和Price
。
還可以按另一種類型(類、結(jié)構(gòu)或另一個匿名類型)的對象定義字段。 它通過使用保存此對象的變量來完成,如以下示例中所示,其中兩個匿名類型是使用已實例化的用戶定義類型創(chuàng)建的。 在這兩種情況下,匿名類型 shipment
和 shipmentWithBonus
中的 product
字段的類型均為 Product
,其中包含每個字段的默認值。 bonus
字段將是編譯器創(chuàng)建的匿名類型。
var product = new Product();
var bonus = new { note = "You won!" };
var shipment = new { address = "Nowhere St.", product };
var shipmentWithBonus = new { address = "Somewhere St.", product, bonus };
在上述例子中我們可以看到,匿名類本身的屬性通常不需要定義類型,而類型是由編譯器自己決定的。而已有類型定義的變量則是其對應的類型,例如product
的類型是Product
,而bonus
的類型則是匿名類。
由于匿名類的類型是不定的,因此使用var
關(guān)鍵字來定義這個匿名類。
如果想要修改匿名類中的屬性,可以使用with表達式來實現(xiàn):
var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
var Banana = apple with {}; // 使用with空值可以直接賦值
(注意,with和record只能在C# 9以上的版本使用,unity中暫時不可用)
記錄
記錄是一個類或者結(jié)構(gòu)體,通過添加record
修飾符聲明,
在下列情況下,請考慮使用記錄而不是類或結(jié)構(gòu):
- 你想要定義依賴值相等性的數(shù)據(jù)模型。
- 你想要定義對象不可變的類型。
值相等性的意思是兩個作為相等比較的值,只有當它們類型相等,且屬性的屬性名相等且屬性值相等,才能視為相等。類似于內(nèi)部的鍵值對需要完全相等。更具體的來說,一般而言的相等性是引用相等性,其相等比較在于:如果兩個類型引用同一個變量則認為它們相等。
不可變性就是在對象實例化后禁止更改該對象的任何屬性或字段值。
引用相等和值相等
為了清楚引用相等和值相等,請看下面幾個例子:
Product A = new Product();
Product B = new Product();
if (A == B)
{
Debug.Log(1); --輸出不了
}
第一個例子中,A==B是false的,因為A和B作為引用類型,它們的引用不相等
Product A = new Product();
Product B = A;
if (A == B)
{
Debug.Log(1); --輸出1
}
在第二個例子中,B引用A,而A引用的是new Product(),所以二者引用相等。
值相等應當不必解釋了,就是平常意義上的相等
record聲明
var phoneNumbers = new string[2];
Person person1 = new("Nancy", "Davolio", phoneNumbers);
Person person2 = new("Nancy", "Davolio", phoneNumbers);
Console.WriteLine(person1 == person2); // output: True
person1.PhoneNumbers[0] = "555-1234";
Console.WriteLine(person1 == person2); // output: True
Console.WriteLine(ReferenceEquals(person1, person2)); // output: False
接口
接口是高度抽象的,我們可以使用interface
關(guān)鍵字定義一個接口,在接口中我們只應當定義函數(shù)頭,其內(nèi)部實現(xiàn)則完全由繼承了接口的類來實現(xiàn)。
interface IEquatable<T>
{
bool Equals(T obj);
}
一個接口同樣可以繼承其他的接口,當一個接口繼承多個其他接口后,這個接口被稱為派生接口,其他接口被稱為基接口。一個繼承了派生接口的類,不僅需要實現(xiàn)派生接口中的所有方法,也需要實現(xiàn)基接口中的所有方法:
interface IBaseInterface
{
void BaseMethod();
}
interface IEquatable : IBaseInterface
{
bool Equals();
}
public class TestManager : IEquatable
{
bool IEquatable.Equals()
{
throw new NotImplementedException();
}
void IBaseInterface.BaseMethod()
{
throw new NotImplementedException();
}
}
delegate 委托
委托的使用通常需要將方法作為參數(shù)傳遞給其他方法時??梢詫⑽幸暈檎{(diào)用一個事件時會觸發(fā)的方法。相當于我們不必在事件或者函數(shù)內(nèi)部定義一個回調(diào)參數(shù),而只需要把委托方法附加上去即可。
相較于接口,委托更適合靈活的方法組合,可以添加給多個事件。用我個人的理解來比喻,點餐是一個事件,假設(shè)這個飯店能APP點餐和服務員點餐,而APP只能點套餐,服務員可以一個菜一個菜給你加?,F(xiàn)在我們分別要用委托和接口實現(xiàn)這個事件。委托像是有個服務員幫你點餐,你只需要說想吃什么,服務員就會幫你記下然后通知后廚。而接口像捆綁了一堆的套餐,如果點了雞腿套餐,那一定會給你雞腿+米飯,點了豬排套餐一定給你豬排+米飯。而使用委托,如果我們想要雞腿+米飯,我們也可以讓服務員給我們增加兩個菜(方法),一個是雞腿,一個是米飯。但是如果我們想要的菜品多,且和套餐重復的話,那么接口可以更好的實現(xiàn)。但如果我們想要靈活地點菜,例如我想要雞腿+米飯,用接口實現(xiàn)了,然后我又想要一個豬排,如果用接口實現(xiàn)那接口會給我豬排+米飯。如果我只想要豬排那就應該叫服務員來,只點一個豬排。
委托可以和方法綁定,當觸發(fā)委托時自動觸發(fā)綁定的方法
// Declare a delegate:
delegate void Del(int x);
// Define a named method:
void DoWork(int k) { /* ... */ }
// Instantiate the delegate using the method as a parameter:
Del d = obj.DoWork;
合并委托/多路廣播委托
不同的委托之間可以合并:
CustomDel hiDel, byeDel, multiDel, multiMinusHiDel;
hiDel = Hello;
byeDel = Goodbye;
multiDel = hiDel + byeDel;
multiMinusHiDel = multiDel - hiDel;
委托的合并直接使用加法即可,簡單來說委托A+委托B =合并委托C,而合并委托C可以使用減法把委托A或者委托B減掉。加法就像服務員A和服務員B跟廚師說1號桌要XXX菜,2號桌要XXX菜,廚師肯定是哪桌先點餐哪桌先上菜。減法就是服務員B過來又說2號桌不要了,你別做了,那廚師只做服務員A委托的1號桌的菜就好了。文章來源:http://www.zghlxwxcb.cn/news/detail-624760.html
在調(diào)用多播委托時,它會按順序調(diào)用列表中的委托。 只能合并相同類型的委托。文章來源地址http://www.zghlxwxcb.cn/news/detail-624760.html
到了這里,關(guān)于【C#學習筆記】引用類型(1)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!