C#相等性比較

来源:http://www.cnblogs.com/shouce/archive/2016/03/17/5285957.html
-Advertisement-
Play Games

本文闡述C#中相等性比較,其中主要集中在下麵兩個方面 ==和!=運算符,什麼時候它們可以用於相等性比較,什麼時候它們不適用,如果不使用,那麼它們的替代方式是什麼? 什麼時候,需要自定一個類型的相等性比較邏輯 在闡述相等性比較,以及如何自定義相等性比較邏輯之前,我們首先瞭解一下值類型比較和引用類型比較


本文闡述C#中相等性比較,其中主要集中在下麵兩個方面

==和!=運算符,什麼時候它們可以用於相等性比較,什麼時候它們不適用,如果不使用,那麼它們的替代方式是什麼?

什麼時候,需要自定一個類型的相等性比較邏輯

在闡述相等性比較,以及如何自定義相等性比較邏輯之前,我們首先瞭解一下值類型比較和引用類型比較

值類型比較對比引用類型比較

C#中的相等性比較有兩種:

  • 值類型相等,兩個值在某種場景下相等
  • 引用類型相等,兩個引用指向同一個對象

預設情況下,

  • 值類型使用值類型相等
  • 引用類型使用引用相等

實際上,值類型只能使用值相等(除非值類型進行了裝箱操作)。看一個簡單的例子(比較兩個數字),運行的結果為True

int x = 5, y = 5;
Console.WriteLine(x == y);

預設地,引用類型使用引用相等。比如下麵的例子:返回False

object x = 5, y = 5;
Console.WriteLine(x == y);

如果x和y指向同一個對象,那麼將返回True:

object x = 5, y = x;
Console.WriteLine(x == y);

相等性的標準

下麵三個標準用於實現相等性比較:

  • ==和!=運算符
  • object中的虛方法Equals
  • IEquatable<T>介面

下麵我們來分別闡述

1. ==和!=運算符

使用==和!=的原因是它們是運算符,它們通過靜態函數實現相等性比較。因此,當你使用==或!=時,C#在編譯時就決定了所比較的類型,而且不會執行任何虛方法(Object.Equals)。這是大家所期望的相等行比較。比如在第一個數字比較的例子中,編譯器在編譯時就決定了執行==運算的類型是int類型,因為x和y都是int類型。

而第二個例子,編譯器決定執行==運算的類型是object類型,因為object是類(引用類型),因此對象的==運算符採取引用相等去比較x和y。那麼結果就返回False,這是因為x和y指向堆上不同的對象(被裝箱的int)

 

2. Object.Equals虛方法

為了正確地比較第二個例子中的x和y,我們可以使用Equals虛方法。System.Object中定義了Equals虛方法,它適用於所有類型

object x = 5, y = 5;
Console.WriteLine(x.Equals(y));

Equals在程式運行時決定比較的類型--根據對象的實際類型進行比較。在上面的例子中,會調用Int32的Euqals方法,該方法使用值相等進行比較,所以上面的例子返回True。如果x和y是引用類型,那麼調用引用相等進行比較;如果x和y是結構類型,那麼Equals會調用結構每個成員對應類型的Equals方法進行比較。

看到這裡,你可能會想,為什麼C#的設計者不把==設計成virtaul,從而使其與Equals一樣,以避免上訴缺陷。這是因為:

  • 如果第一個運算對象是null,Equals方法會拋出NullReferenceException異常;而靜態的運算符則不會
  • 因為==運算符在編譯時決定了比較類型(靜態解析比較類型),那麼它的執行就非常快。這也就使得編寫大量運算代碼去執行相等性比較時對性能不會帶來太大的影響
  • 有時候,==和Equals適用於不同的場景的相等性比較。(後續的內容會涉及)

簡而言之,複雜的設計反映了複雜的場景:相等的概念涉及到許多場景。

 

而Euqals方法,適用於比較兩個未知類型的對象,下麵的這個方法就適用於比較任何類型的兩個對象:

public static bool AreEqual(object obj1, object obj2)
{
    return obj1.Equals(obj2);
}

但是,該函數不能處理第一個參數是null的情形,如果第一個函數是null,你會得到NullReferenceException異常。因此我們需要對該函數進行修改:

public static bool AreEqual(object obj1, object obj2)
{
    if (obj1 == null)
        return obj2 == null;
    return obj1.Equals(obj2);
}

 

object的靜態Equals方法

object類還定義了一個靜態Equals方法,它的作用與AreEquals方法一樣。

複製代碼
public static bool Equals(Object objA, Object objB)
{
    if (objA==objB) {
        return true;
    }
    if (objA==null || objB==null) {
        return false;
    }
    return objA.Equals(objB);
}
複製代碼

這樣就可以對編譯時不知道類型的null對象進行安全地比較。

複製代碼
object x = 5, y = 5;
Console.WriteLine(object.Equals(x, y)); // -> True
x = null;
Console.WriteLine(object.Equals(x, y)); // -> False
y = null;
Console.WriteLine(object.Equals(x, y)); // -> True

Console.WriteLine(x.Equals(y)); // -> NullReferebceException, because x is null
複製代碼

請註意,當編寫Generic類型時,下麵的代碼將不能通過編譯(除非把==或!=運算符替換成Object.Equals方法的調用):

複製代碼
public class Test<T> : IEqualityComparer<T>
{
    T _value;

    public void SetValue(T newValue)
    {
        // Operator '!=' cannot be applied to operands of type 'T' and 'T'
        // it should be : if(!object.Equals(newValue, _value))
        if (newValue != _value)
            _value = newValue;
    }
}
複製代碼

object的靜態ReferenceEquals方法

有時候,你需要強行比較兩個引用是否相等。這個時候,你就需要使用object.ReferenceEquals:

internal class Widget
{
public string UID { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return this == null;

if (!(obj is Widget))
return false;
Widget w = obj as Widget;
return this.UID == w.UID;
}

public override int GetHashCode()
{
return this.UID.GetHashCode();
}

public static bool operator == (Widget w1, Widget w2)
{
return w1.Equals(w2);
}

public static bool operator !=(Widget w1, Widget w2)
{
return !w1.Equals(w2);
}
}

static void Main(string[] args)
{

Widget w1 = new Widget();
Widget w2 = new Widget();
Console.WriteLine(w1==w2); // -> True
Console.WriteLine(w1.Equals(w2)); // -> True
Console.WriteLine(object.ReferenceEquals(w1, w2)); // -> False

Console.ReadLine();
}

Basic ReferenceEquals


複製代碼
internal class Widget 
{
    public string UID { get; set; }
    public override bool Equals(object obj)
    {
        if (obj == null)
            return this == null;

        if (!(obj is Widget))
            return false;
        Widget w = obj as Widget;
        return this.UID == w.UID;
    }

    public override int GetHashCode()
            {
                return this.UID.GetHashCode();
            }

    public static bool operator  == (Widget w1, Widget w2)
            {
                return w1.Equals(w2);
            }

    public static bool operator !=(Widget w1, Widget w2)
            {
                return !w1.Equals(w2);
            }
}

static void Main(string[] args)
{

    Widget w1 = new Widget();
    Widget w2 = new Widget();
    Console.WriteLine(w1==w2); // -> True
    Console.WriteLine(w1.Equals(w2)); // -> True
    Console.WriteLine(object.ReferenceEquals(w1, w2)); // -> False 

    Console.ReadLine();
}
複製代碼

 

之所以調用ReferenceEquals方法,這是因為自定義類Widget重寫了object類的虛方法Equals;此外,該類還重寫了操作符==和!=,因此執行==時操作也返回True。所以,調用ReferenceEquals可以確保返回引用是否相等。

3. IEquatable<T>介面

調用object.Equals方法實際上對進行比較的值類型進行了裝箱操作。在對性能有較高要求的場景,那麼就不適合使用這種方式。從C#2.0開始,通過引入IEquatable<T>介面來解決這個問題

public interface IEquatable<T>
{
    bool Equals(T other);
}

當實現IEquatable介面之口,調用介面方法就等同於調用objet的虛方法Equals,但是介面方法執行地更快(不需要類型轉換)。大多數.NET基本類型都實現了IEquatable<T>介面,你還可以為Generic類型添加IEquatable<T>限制

複製代碼
internal class Test<T> where T : IEquatable<T>
{
    public bool IsEqual(T t1, T t2)
    {
        return t1.Equals(t2);
    }
}
複製代碼

如果,我們移除IEquatable<T>限制,Test<T>類仍可以通過編譯,但是t1.Equals(t2)將使用object.Equals方法。

4. 當Equals結果與==的結果不一致

在前面的內容中,我們已經提到有時候,==或equals適用於不同的場景。比如:

double x = double.NaN;
Console.WriteLine(x == x); // False
Console.WriteLine(x.Equals(x)); // True

這是因為double類型的==運算符強制NaN不等於其他任何值,即使另外一個NaN。從數學的角度來講,兩個確實不相等。而Equals方法,因為具有對稱性,所以x.Equals(x)總返回True。

集合與字典正是依賴於Equals的對稱性,否則就不能找到已經保存在集合或字典中的元素。

對於值類型而言,Equals和==很少出現不同的相等性。而在引用類型中,則比較常見。一般地,引用類型的創建者重寫Equals方法執行值相等比較,而保留==執行引用相等比較。比如StringBuilder類就是這樣的:

StringBuilder buffer1 = new StringBuilder("123");
StringBuilder buffer2 = new StringBuilder("123");
Console.WriteLine(buffer1 == buffer2); // False
Console.WriteLine(buffer1.Equals(buffer2)); // True

 

比較自定義類型

回顧一下預設的比較行為

  • 值類型使用值相等
  • 引用類型使用引用相等

進一步,

  • 結構類型的equals方法會根據每個欄位的類型進行相等行比較

有時候,在創建類型時,需要重寫上述行為,一般在下麵兩種情形下需要重寫:

  • 更改相等的意義
  • 提高結構類型的比較速度

 

1)更改相等的意義

當預設的==和Equals不適用(不符合自然規則,或悖離了使用者的期望)於自定義類型時,就需要更改相等的意義。比如DateTimeOffset結構,其有兩個私有成員:一個DateTime類型的UTC,以及int類型的offset。如果是你在創建DateTimeOffset類型,那麼你很可能只要UTC欄位相等即可,而不去比較Offset欄位。另外一個例子就是支持NaN的數字類型,比如float和double,如果你來創建這兩個類型,你可能會希望NaN也是可以進行比較的。

而對於Class類型,很多時候,使用值比較更有意義。尤其是一些包含較少數據的類,比如System.Uri或System.String

2)提高結構類型的比較速度

結構類型的預設比較演算法相對較慢。通過重寫Equals方法可以提高5%的性能。而重載==運算和實現IEquatable<T>可以在不裝箱操作的情況下實現相等性比較,這使得提高5%性能變得可能。

對於自定義相等比較,有一個特殊的情形,更改結構類型的hashing演算法後,hashtable可以獲得更好的性能。這是因為hashing演算法和相等性比較都發生在棧上。

3)如何重寫相等

總地來說,有下麵三種方式:

  • 重寫GetHashcode()和Equals()
  • 【可選】重載!=和==
  • 【可選】實現IEquatable<T>

I)重寫GetHashCode

object對象的虛方法GetHashCode,也就僅僅對於Hashtable類型和Dictionary<TKey,TValue>類型有益。

這兩個類型都是哈希表集合,集合中的每個元素都是一個鍵值用於存儲元素和獲取元素。哈希表使用了一個特定的策略以有效地基於元素的鍵值分配元素。這就要求每個鍵值都有一個Int32數(或哈希碼)。哈希碼不僅對於每個鍵值是唯一的,而且還必須有較好的性能。哈希表認為object類定義的GetHashCode方法已經足夠了,因此這兩個類型都省略了獲取哈希碼的方法。

無論值類型還是引用類型,都預設實現了GetHashCode方法,所以你不用重寫這個方法,除非你需要重寫Equals方法。(因此,如果你重寫了GetHashCode方法,那麼你肯定是需要重寫Equals方法)。

是否需要重寫GetHashCode方法,可以參考下麵的規則:

  • 如果Equals方法返回True是,兩個比較的對象必須返回相同的哈希碼
  • 不允許拋出異常
  • 除非對象變化了,那麼重覆的對一個對象調用GetHashCode方法應返回相同的哈希碼

為了提高哈希表的性能,GetHashCode需要重寫以防止不同的值返回相同的哈希碼。也這就說明瞭為什麼需要對結構類型需要重寫Equals和GetHashCode方法,因此這樣重寫比預設的哈希演算法更有效率。結構類型的GetHashCode方法的預設實現是在運行時才發生,而且很可能基於結構的每個成員而實現。

複製代碼
// char type
public override int GetHashCode() {
    return (int)m_value | ((int)m_value << 16);
}

// int32
public override int GetHashCode() {
    return m_value;
}
複製代碼

而類(class)類型,GetHashCode方法的預設實現基於內部對象標識,這個標識在CLR中對於每個對象實例都是唯一的。

public virtual int GetHashCode()
{
    return RuntimeHelpers.GetHashCode(this);
}

II)重寫Equals

object.Equal的規定(公理)如下:

  • 一個對象不能和null相等(除非對象是nullable類型)
  • 相等性是對稱的(一個對象等於自身)
  • 相等性是可交換的(如果a等於b,那麼b也等於a)
  • 相等性是可傳遞的(如果a等於b,b等於c,那麼a等於c)
  • 相等性是可重覆的,並且是可靠的(不會拋出異常)

III)重載==和!=

除了可重寫Equals,還可以重載等於和不等於運算符。

對於結構類型,基本都重載了等於和不等於運算符,如果不重載它們,那麼對於結構類型,等於和不等於將返回錯誤的結果;

而對於類(class)類型,有兩種處理方式:

  • 不重載==和!=,因為它們會執行引用相等
  • 重載==和!=,使其與Equals一致

第一種實現適用於大多數自定義類型,特別是可變(mutable)類型。它確保了自定義類型符合==和!=就應該執行引用相等性的比較,從而不會誤導這些自定義的使用者。再次回顧一下前面舉過的StringBuilder例子

StringBuilder buffer1 = new StringBuilder("123");
StringBuilder buffer2 = new StringBuilder("123");
Console.WriteLine(buffer1 == buffer2); // False, Reference equality
Console.WriteLine(buffer1.Equals(buffer2)); // True, Value equality

而第二種實現適用於使用者永遠都不希望自定義類型執行引用相等。一般地這些都類型都是不可變(immutable)類型,比如string類型和System.Uri類型,當然也包含一些引用類型。

III)實現IEquatable<T>

為了保持完整性,建議在重寫Equals方法時,同時實現IEquatable<T>介面。介面方法的結果應當與自定義重寫後Equals方法的結果一致。如果你已經重寫了Equals方法,那麼實現IEquatable<T>不需要額外的實現代碼(直接調用Equlas方法即可)

複製代碼
internal class Staff : IEquatable<Staff>
{
    public string FirstName { get; set; }

    // implements IEquatable<Staff>
    public bool Equals(Staff other)
    {
        return this.FirstName.Equals(other.FirstName);
    }

    // override Equals
    public override bool Equals(object obj)
    {
        if (obj == null)
            return this == null;

        if (!(obj is Staff))
            return false;

        Staff s = obj as Staff;

        return this.FirstName == s.FirstName;
    }

    // override GetHashCode
    public override int GetHashCode()
    {
        return this.FirstName.GetHashCode();
    }            
}
複製代碼

IV)可插入的相等比較器

如果你希望一個類型在某一個特定的場景下使用不同的比較,那麼你可以使用可插件式的IEqualityComparer。它特別適用於集合類。(後續有內容介紹)

 

相等性比較總結

C#類庫中,為相等性比較設計了三個介面:IEquatable<T>,IEqualityComparer,以及IEqualityComparer<T>。

IEqualityComparer與IEqualityComparer<T>的差別很簡單,一個是非Generic的,需要把T轉換成Object,然後調用Object的Equals方法;而後者直接調用T類型實例的Equals方法。

那麼IEquatable<T>和IEqualityComparer<T>有什麼差別,分別適用於什麼場景呢?

1. IEquatable<T>用於比較與自己類型相同的另一個對象是否相等;而IEqualityComparer<T>則用於比較兩個相同類型的實例是否相等。

2. 如果兩個實例是否相等只有一種可能,或者有幾個是否相等的比較但只有其中一個更有意義,那麼應該選擇IEquatable<T>,T類型自己實現IEquatable<T>介面。因此IEquatable<T>的實例自己就知道該如何比較自己和另外一個實例。與之相反,如果需要比較的實例之間存在多個相等性比較,那麼IEqualityComparer<T>更適合這種情況;這個介面不會由T類型實現,相反需要一個外部的類實現IEqualityComparer<T>介面。因為,當比較兩個類型實例是否相等時,因為T類型內部不知道如何比較,那麼你就需要顯示地指定一個IEqualityComparer<T>實例用於執行相等性比較從而滿足特定的需求。

3. 例子

internal class Staff : IEquatable<Staff>
{
public string FirstName { get; set; }
public string Title { get; set; }
public string Dept { get; set; }

public override string ToString()
{
return string.Format(
"FirstName:{0}, Title:{1}, Dept:{2}",
FirstName, Title, Dept);
}

// implements IEquatable<Staff>
public bool Equals(Staff other)
{
return this.FirstName.Equals(other.FirstName);
}


// override Object.GetHashCode
public override int GetHashCode()
{
return this.FirstName.GetHashCode();
}

}

internal class StaffTitleComparer : IEqualityComparer<Staff>
{
public bool Equals(Staff x, Staff y)
{
return x.Title == y.Title;
}

public int GetHashCode(Staff obj)
{
return obj.Title.GetHashCode();
}

}

internal class StaffDeptComparer : IEqualityComparer<Staff>
{
public bool Equals(Staff x, Staff y)
{
return x.Dept == y.Dept;
}

public int GetHashCode(Staff obj)
{
return obj.Dept.GetHashCode();
}

}


static void Main(string[] args)
{

IList<Staff> staffs = new List<Staff>
{
new Staff{FirstName="AAA", Title="Manager", Dept="Sale"},
new Staff{FirstName="BBB", Title="Accountant", Dept="Finance"},
new Staff{FirstName="BBB", Title="Accountant", Dept="Finance"},
new Staff{FirstName="AAA", Title="Sales", Dept="Sale"},
new Staff{FirstName="ABA", Title="Manager", Dept="HR"}
};
Print("All Staffs", staffs);
Print("No duplicated first name", staffs.Distinct());
Print("No duplicated title", staffs.Distinct(new StaffTitleComparer()));
Print("No duplicated department", staffs.Distinct(new StaffDeptComparer()));

Console.ReadLine();
}

private static void Print(string group, IEnumerable<Staff> staffs)
{
Console.WriteLine(group);
foreach (Staff s in staffs)
Console.WriteLine(s.ToString());

Console.WriteLine();
}

Overall

 

--update--

最後一個例子,還可以通過擴展IEnumeable<T>來實現DistinctBy:

複製代碼
public static class IEnurambleExtension
{
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    { 
        HashSet<TKey> keys = new HashSet<TKey>();

        foreach (TSource element in source)
            if (keys.Add(keySelector(element))) 
                yield return element;
    }
}
複製代碼

可以這樣使用

staffs.DistinctBy(s => s),註意,staff類需要實現IEquatable<T>(或重寫Equals和GetHashCode)

staffs.DistinctBy(s => s.Dept),這就省去了編寫StaffDeptComparer類

進一步,如果staff的某個欄位是一個類,那麼這個類同樣需要實現IEquatable<T>(或重寫Equals和GetHashCode)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 二、Makefile(下)01.make常用內嵌函數函數調用  $(function arguments)$(wildcard PATTERN)  當前目錄下匹配模式的文件  例如:src=$(wildcard *.c)$(patsubst PATTERN,REPLACEMENT,TEXT)  模式
  • 鑒於前段時間系統出了點問題,然後直接將盤抹了,來個徹底乾凈的系統重裝。這裡敲下過程。(網路恢復太慢了,我整整一個晚上竟然沒down下來,惱怒了,直接U盤裝) First,系統盤製作: 1.首先需要有:一個不小於8G的U盤,運行OS X 系統的電腦(U盤最好先使用磁碟工具進行抹盤,格式選擇OS 擴展(
  • 二、makefile(上)01.make工具  利用make工具可以自動完成編譯工作。這些工作包括:如果修改了某幾個源文件,則只重裝新編譯這幾個源文件;如果某個頭文件被修改了,則重新編譯所有包含該頭文件的源文件。利用這個自動編譯可大大簡化開發工作,避免不必要的重新編譯。  make工具通過一個稱為M
  • 轉自:http://engrmosaic.uncc.edu/mosaic-anywhere/remote-desktop-file-format The new Terminal Services client (version 2600 and newer, 5.1.2600.x) introdu
  • Nginx 訪問日誌輪詢切割腳本         配置www.conf     配置bbs.conf     配置blog.conf 測試     配置定時任務使得每天0點整執行腳本    
  • 初學者對 : Controller where T : class,new() 不理解 ,我是初學者的時候也一樣不理解,在此做個簡單的解釋:這是類型參數約束,.NET支持的類型參數約束有以下五種: where T : struct | T必須是一個結構類型 where T : class | T必須
  • 異常處理彙總-前端系列 http://www.cnblogs.com/dunitian/p/4523015.html 舉個例子 重點:display:flex (參考:http://www.360doc.com/content/14/0811/01/2633_400926000.shtml) 效果
  • 一、前言 異常處理是每個系統必不可少的一個重要部分,它可以讓我們的程式在發生錯誤時友好地提示、記錄錯誤信息,更重要的是不破壞正常的數據和影響系統運行。異常處理應該是一個橫切點,所謂橫切點就是各個部分都會使用到它,無論是分層中的哪一個層,還是具體的哪個業務邏輯模塊,所關註的都是一樣的。所以,橫切關註點
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...