C++11 智能指針 shared_ptr

来源:https://www.cnblogs.com/champrin/archive/2023/01/17/17056814.html
-Advertisement-
Play Games

C++11 智能指針 shared_ptr Written on 2023-01-16 個人學習智能指針記錄合集: C++11 智能指針 C++11 智能指針 shared_ptr C++11 智能指針 unique_ptr C++11 智能指針 weak_ptr std::shared_ptr 共 ...


C++11 智能指針 shared_ptr

Written on 2023-01-16

個人學習智能指針記錄合集:

std::shared_ptr 共用智能指針,也被稱為計數智能指針。

共用智能指針會記錄有多少個共用智能指針指向同一個對象,當這個數為 0 的時候,程式自動的預設釋放(析構)這個對象,記錄有多少個的這個方法叫做引用計數

共用智能指針可以有多個共用智能指針同時管理同一個對象。

舉個慄子

普通指針管理

#include <iostream>
#include <memory>
using namespace std;

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
private:
    int m_age = 0; 
}; 

int main()
{
    {
        Person *p = new Person(18);
    }

    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 18

Before return

**/

依輸出可見,p指向的對象並沒有被析構,因為並沒有析構函數中的列印,這造成了記憶體泄漏。

shared_ptr智能指針管理

// ...
int main()
{
    {
        shared_ptr<Person> sPtr1 {new Person(18)};
    }

    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 18
Destructor: person's age = 18

Before return

**/

依輸出可見,離開了程式塊的作用域後,析構函數中的列印體現出來了,sPtr1管理的對象自動的被析構了。

獲取引用計數

long use_count() const noexcept;

shared_ptrnullptr 時,返回為 0。

初始化

創建空管理的 shared_ptr

constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
  • std::nullptr_t:空指針nullptr

創建對非數組對象和數組對象管理的 shared_ptr

template< class Y >
explicit shared_ptr( Y* ptr );
  • Y:動態分配的對象的類型
// ...
int main()
{
    shared_ptr<int> sPtr1; // 創建空管理的shared_ptr
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    shared_ptr<int> sPtr2(nullptr); // 創建空管理的shared_ptr
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    
    shared_ptr<int> sPtr3(new int(100)); // 創建對非數組對象管理的shared_ptr
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    
    shared_ptr<int> sPtr4(new int[10]); // 創建對數組對象管理的shared_ptr
    cout << "sPtr4 use count = " << sPtr4.use_count();
    return 0;
}
/** 輸出:
sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 1
sPtr4 use count = 1
**/

解釋:
sPtr1sPtr2都為nullptr,故引用計數都為 0;
sPtr3sPtr4所管理的對象,都只有一個 shared_ptr 管理,故引用計數都為 1。

創建指定刪除器的 shared_ptr

template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );

template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
shared_ptr( Y* ptr, Deleter d, Alloc alloc );

template< class Deleter, class Alloc >
shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc );
  • Deleter:刪除器,在引用計數為 0 時,shared_ptr 會自動的執行刪除器
  • Alloc:分配器(Allocator)
  1. 預設刪除器:預設對指向的記憶體地址進行釋放,即析構掉這塊地址的內容
    1. 非數組類型,以 delete ptr 為預設刪除器
    2. 數組類型,以 delete[] ptr 為預設刪除器
  2. 指定刪除器:能夠自行指定引用計數為 0 時,要做什麼,比如有些場景不需要對指向的記憶體進行釋放,比如關閉指向文件,關閉指向套接字
    1. 指定刪除器的函數結構:
      void deletePtr(T* p){ ... }
      
    2. 也可使用 Lambda表達式

例,對於文件的使用場景,不是直接delete文件,而是關閉文件:

// ...
void closeFile(FILE* fp) 
{
    if (fp == nullptr) return;
    fclose(fp);
    cout << "File closed" << endl;
}
int main() 
{
    FILE* fp = fopen("data.txt" ,"w");
    shared_ptr<FILE> sfp{fp,closeFile};
    if (sfp == nullptr)
        cout << "Failed opened" << endl;
    else 
        cout << "File opened" << endl;
    return 0;
}

創建通過現有共用智能指針的 shared_ptr

主要是通過複製構造函數和移動構造函數std::move()

shared_ptr( const shared_ptr& r ) noexcept;

template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;

shared_ptr( shared_ptr&& r ) noexcept;

template< class Y >
shared_ptr( shared_ptr<Y>&& r ) noexcept;    

例,

// ...
int main()
{
    shared_ptr<int> sPtr1(new int(100));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl << endl;
    // 創建通過現有現有共用智能指針的 shared_ptr
    // 通過拷貝構造函數創建
    shared_ptr<int> sPtr2(sPtr1);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl << endl;
    // 通過賦值運算符創建
    shared_ptr<int> sPtr3 = sPtr1;
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl << endl;
    // 通過移動構造函數創建
    shared_ptr<int> sPtr4(move(sPtr1));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    std::shared_ptr<int> sPtr5 = move(sPtr2);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl;
    cout << "sPtr5 use count = " << sPtr5.use_count();
    return 0;
}
/** 輸出:
sPtr1 use count = 1

sPtr1 use count = 2
sPtr2 use count = 2

sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3

sPtr1 use count = 0
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 3

sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 3
sPtr4 use count = 3
sPtr5 use count = 3
**/

解釋:
sPtr2sPtr3都是通過對sPtr1執行了複製構造函數,因此sPtr1的引用計數一次增加 1;sPtr2sPtr3同理。
通過move(sPtr1),使得sPtr1釋放了被管理對象的所有權,此時sPtr1被設置為nullptr,因此sPtr1引用計數為 0;sPtr2同理。

通過 std::make_shared

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
  • T:非數組,指針指向的數據類型
  • Args&&... argsT 的構造函數參數列表

這種初始化效率更高,在 C++17 之前的編譯器更安全

// ...
int main()
{
    shared_ptr<int> sPtr1 = make_shared<int>(100);
    shared_ptr<int> sPtr2(make_shared<int>(200));
    shared_ptr<int> sPtr3{make_shared<int>(300)};
    return 0;
}

通過 .reset( ptr )

使用.reset( ptr ),使得釋放對shared_ptr原管理對象的所有權,轉為對新對象管理的所有權。

void reset() noexcept;

template< class Y >
void reset( Y* ptr );

template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );

註意.reset( ptr )若傳入的所指向的對象已被占有,程式會異常。

// ...
int main()
{
    shared_ptr<int> sPtr1(new int(100));
    shared_ptr<int> sPtr2 = sPtr1;
    shared_ptr<int> sPtr3 = sPtr2;
    shared_ptr<int> sPtr4 = sPtr1;
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    
    sPtr4.reset();
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    
    sPtr3.reset(new int(100));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count();
    
    std::shared_ptr<int> sPtr5;
    sPtr5.reset(sPtr1.get()); // 異常
    return 0;
}
/** 輸出:
sPtr1 use count = 4
sPtr2 use count = 4
sPtr3 use count = 4
sPtr4 use count = 4

sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 0

sPtr1 use count = 2
sPtr2 use count = 2
sPtr3 use count = 1
sPtr4 use count = 0
**/

解釋:

  1. 通過複製構造函數後,sPtr1-4的引用計數均為 4。
  2. sPtr4使用了reset()後,釋放被管理對象的所有權,被管理對象的shared_ptr個數減 1,sPtr4被設置為nullptr,同時,sPtr1-3的引用計數均變為 3。
  3. sPtr3使用了reset(new int(100))後,釋放原被管理對象的所有權,被管理對象的shared_ptr個數減 1,sPtr4被設置為新的管理對象int(100),同時,sPtr1-3的引用計數均變為 2。
  4. sPtr5reset傳入的所指向的對象已被占有,程式異常,沒有正常結束。

.reset() 釋放被管理對象的所有權

用於取消shared_ptr對管理對象的所有權;
當這個對象被shared_ptr管理的數量為 0,會執行刪除器。

使用.reset(),會使得shared_ptr設置為nullptr

獲取原始儲存的指針

T* get() const noexcept;

解引用存儲的指針 operator* and operator->

可以像普通指針一樣,使用shared_ptr對所管理的對象進行訪問。

// ...
int main()
{
    shared_ptr<int> sPtr1 {new int(100)};
    cout << *sPtr1 << endl << endl;
    
    int *i = sPtr1.get();
    cout << *i << endl;
    cout << *sPtr1 << endl << endl;
    
    *i = 200;
    cout << *i << endl;
    cout << *sPtr1 << endl << endl;
    
    *sPtr1 = 300;
    cout << *i << endl;
    cout << *sPtr1;
    return 0;
}
/** 輸出:
100

100
100

200
200

300
300
**/

直觀展示自動管理記憶體

// ...
int main()
{
    shared_ptr<Person> sPtr1 {make_shared<Person>()};
    shared_ptr<Person> sPtr2 {make_shared<Person>(18)};
    shared_ptr<Person> sPtr3 {make_shared<Person>(22)};
    
    shared_ptr<Person> sPtr4 = sPtr1;
    sPtr1.reset();
    sPtr2.reset();
    
    sPtr3.reset(new Person(19));
    
    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 0
Constructor: person's age = 18
Constructor: person's age = 22
Destructor: person's age = 18
Constructor: person's age = 19
Destructor: person's age = 22

Before return
Destructor: person's age = 0
Destructor: person's age = 19

**/

解釋:

  1. 定義了三個shared_ptrsPtr1sPtr2sPtr3,它們管理的對象分別為age = 0age = 18age = 22,列印了三行Person對象的構造函數中的輸出
  2. 通過賦值運算符,使得sPtr4同時管理sPtr1管理的對象
  3. 釋放sPtr1被管理對象的所有權,此時因為sPtr4還在管理原sPtr1管理的對象age = 0,因此age = 0對象並沒有被析構
  4. 釋放sPtr2被管理對象的所有權,此時因為age = 18對象沒有任何shared_ptr進行管理,age = 18對象被析構,列印了age = 18對象的析構函數中的輸出
  5. 釋放sPtr3被管理對象的所有權,sPtr3轉為管理age = 19的對象;是先構造age = 19對象,後再釋放sPtr3被管理對象的所有權,列印了age = 19對象的構造函數中的輸出;此時因為sPtr3原管理的age = 22對象沒有任何shared_ptr進行管理,age = 22對象被析構,列印了age = 22對象的析構函數中的輸出
  6. 在程式返回前Before return,管理age = 0age = 19對象的智能指針sPtr4sPtr3被析構,age = 0age = 19對象沒有智能指針管理,age = 0age = 19對象被析構,列印了它們對象的析構函數中的輸出。

別名

template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

別名用於訪問類的成員變數。

我們需要訪問的是某個實例的成員,因此並不希望在使用這個成員的時候,對應的實例被銷毀了。

使用別名的shared_ptr,增加了對這個實例的控制權,但仍然訪問的是成員。

// ...
struct Person{ int age = 18; };
struct Student{ Person person; };

int main(){
    shared_ptr<Student> studentPtr{make_shared<Student>()};
    cout << "studentPtr use count = " << studentPtr.use_count() << endl;
    shared_ptr<Person> personPtr{studentPtr, &(studentPtr->person)};
    cout << "studentPtr use count = " << studentPtr.use_count() << endl;
    cout << personPtr->age << endl;
    return 0;
}
/** 輸出:
studentPtr use count = 1
studentPtr use count = 2
18

**/

shared_ptr 與函數

按值傳遞

int main(){
	auto func = [](shared_ptr<int> sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}
/** 輸出:
init: use count = 1
value = 100
enter func: use count = 2
exit func: use count = 1

**/

按值傳遞,函數會複製一份參數,因此傳入的sPtr會被覆制一份,造成其對象的引用計數增加 1;
執行完這個函數,複製的那一份sPtr被銷毀,使得其對象的引用計數減少 1;

按引用傳遞

int main(){
	auto func = [](shared_ptr<int> &sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}
/** 輸出:
init: use count = 1
value = 100
enter func: use count = 1
exit func: use count = 1

**/

按引用傳遞,函數不會複製一份參數;
因此若函數內部無其它導致增加引用計數的操作,函數執行過程中引用計數都不會改變。

按引用傳遞,但為const

int main(){
    auto func = [](const shared_ptr<int> &sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
        sPtr.reset(); // error
        sPtr.reset(new Person()); // error
        sPtr.release(); // error
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}

使用const的引用傳遞,不能改變shared_ptr所管理的對象是哪一個,使用.reset()等都會造成編譯錯誤。

返回值為shared_ptr

int main(){
	auto createSPtr = [](int i) -> shared_ptr<Person>{
        shared_ptr<Person> sPtr = make_shared<Person>(i);
        cout << "age = " << i << " use count = " << sPtr.use_count() << endl;
        return sPtr;
    };
    shared_ptr<Person> sPtr = createSPtr(100);
    sPtr->getAge(); cout << "use count = " << sPtr.use_count() << endl;

    // 用作鏈式函數
    createSPtr(200)->getAge();
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 100
age = 100 use count = 1
Person's age = 100
use count = 1
Constructor: person's age = 200
age = 200 use count = 1
Person's age = 200
Destructor: person's age = 200

Before main return
Destructor: person's age = 100

**/

可見當用作鏈式函數時,使用完畢後,unique_ptr會被銷毀,同時被管理的對象也被析構。

管理動態數組需要指定刪除器

shared_ptr 的預設刪除器不支持釋放數組對象,需要指定刪除器。

例,一維數組指定刪除器

    // ...
    shared_ptr<int> ptr(new int[10], [](int* p) {delete[] p; });

同時,也可以使用 std::default_delete<T>() 函數作為刪除器,這個函數內部的刪除功能是通過delete釋放,T 為釋放什麼類型的記憶體的類型。

例,一維數組指定刪除器

    // ...
    shared_ptr<int> ptr(new int[10], default_delete<int[]>());

可以自己封裝模板函數來使 shared_ptr 支持釋放數組對象。

// ...
template <typename T>
shared_ptr<T> arrayShared_ptr(size_t size)
{
    // 返回匿名對象
    return shared_ptr<T>(new T[size], default_delete<T[]>());
}

int main()
{
    shared_ptr<int> sPtr1 = arrayShared_ptr<int>(100);
    shared_ptr<int> sPtr2 = arrayShared_ptr<char>(200);
    return 0;
}

危險行為

仍然可使用delete釋放智能指針管理的對象的地址

可以使用delete釋放shared_ptr管理的對象的地址,但是其它共用指針仍然可能會訪問這塊地址,若訪問了則會出現程式異常,因此應避免使用手動的delete

    // ...
    shared_ptr<int> sPtr1 {new int(100)};
    shared_ptr<int> sPtr2 = sPtr1;
    delete sPtr1.get();
    cout << *sPtr2 << endl; // error,運行時異常

同時存在原始指針和智能指針

如果一個地址記憶體,同時有原始指針和shared_ptr指向它,即使當所有shared_ptr都被銷毀,原始指針還依然存在的情況下,這個地址記憶體仍然會被釋放,若再用原始指針去訪問這個記憶體地址就是訪問了一塊未知地址的內容。

    // ...
	int *i1 = new int{100};
    shared_ptr<int> sPtr1{i1};
    int *i2 = sPtr1.get();
    cout << *i1 << endl;
    cout << *i2 << endl << endl;
    sPtr1.reset();
    cout << *i1 << endl; // 危險行為 
    cout << *i2 << endl; // 危險行為 
	return 0;
/** 輸出:
100
100

1664688 // 這是隨機的,訪問了一塊未知地址的內容
1664688 // 這是隨機的,訪問了一塊未知地址的內容

**/

同時,無論是使用shared_ptr,還是其它的智能指針,都應該避免與原始指針混用。

避免同時存在原始指針和智能指針的解決方案:

    // ...
    int *i = new int{100};
    shared_ptr<int> sPtr1{i};
    i = nullptr;
    delete i;
    cout << *sPtr1 << endl; // ok

使用一個原始指針初始化多個shared_ptr

不能使用一個原始指針初始化多個shared_ptr

    // ...
    int *i = new int{100};
    shared_ptr<int> sPtr1{i};
    shared_ptr<int> sPtr2{i}; // error 編譯通過 運行錯誤

最後

shared_ptr 由於使用引用計數,因此會造成額外的記憶體和性能開銷,因此在性能要求極為苛刻的情況下不適用。

unique_ptr是 0 開銷的智能指針,也能夠自動管理記憶體,但不會造成性能損失。


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

-Advertisement-
Play Games
更多相關文章
  • 抽獎程式 ''' 抽獎程式 使用時可以修改嘉賓名單,然後單機‘開始’和‘停止’按鈕 來控制界面上名單的滾動實現抽獎功能,涉及的模塊主要 有多線程 ''' import itertools import random import threading import time import tkinte ...
  • 摘要:本文主要講解圖像局部直方圖均衡化和自動色彩均衡化處理。這些演算法可以廣泛應用於圖像增強、圖像去噪、圖像去霧等領域。 本文分享自華為雲社區《[Python從零到壹] 五十四.圖像增強及運算篇之局部直方圖均衡化和自動色彩均衡化處理》,作者: eastmount。 一.局部直方圖均衡化 前文通過調用O ...
  • 簡介 限流顧名思義是對流量大小進行限制,防止請求數量超過系統的負載能力,導致系統崩潰,起到保護作用。 現實生活中限流也隨處可見,節假日出門旅行的人數會劇增,對於旅游景點來說往往會不堪重負,如果不進行人數控制,對整個景點的壓力會非常大,游客的體驗也會非常差,還容易出現安全事故等危險。 同樣的在一線城市 ...
  • 轉載:https://blog.csdn.net/tslx1020/article/details/128250777 1、spawn - 冷啟動 frida-trace -U -f com.apple.ExampleCode -m “+[NSURL URLWithString:]" 2、attac ...
  • 伺服器信息 在阿裡雲買了個搶占式的伺服器,地區為華南廣州,系統為Ubuntu 20.04,8核16GB。 安裝Docker 命令如下: $ apt-get update -y $ apt-get upgrade -y $ apt-get install -y docker.io 安裝成功後,檢查一下 ...
  • 2023-01-14 一、Spring底層IOC實現 1、IOC:將對象的控制器反轉給Spring 2、BeanFactory與ApplicationContext (1)BeanFactory:IOC容器的基本實現,是Spring內部的使用介面,是面向Spring本身的,不是提供給開發人員使用的。 ...
  • 牛牛剛剛出生,嗷嗷待哺,一開始他只能學說簡單的數字,你跟他說一個整數,他立刻就能學會。輸入一個整數,輸出這個整數。 ...
  • 本篇文章,我們就一起聊一聊如何來更好的使用緩存,探尋下如何降低緩存交互過程的性能損耗、如何壓縮緩存的存儲空間占用、如何保證多個操作命令原子性等問題的解決策略,讓緩存在項目中可以發揮出更佳的效果。 ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...