從C#3開始,拓展方法這一特性就得到了廣泛的應用。 此功能允許你能夠使用實例方法的語法調用某個靜態方法,以下是一個獲取/創建文件的靜態方法: public static async Task<StorageFile> GetOrCreateFileAsync(this StorageFolder f ...
從C#3開始,拓展方法
這一特性就得到了廣泛的應用。
此功能允許你能夠使用實例方法的語法調用某個靜態方法,以下是一個獲取/創建文件的靜態方法:
public static async Task<StorageFile> GetOrCreateFileAsync(this StorageFolder folder,string name)
{
var item = await folder.TryGetItemAsync(name) as StorageFile;
item ??= await folder.CreateFileAsync(name);
return item;
}
可以採取如下方式調用此方法,但可讀性較差:
await Extensions.GetOrCreateFileAsync(folder, "FileName");
通過為方法的第一個參數添加this
標記,我們還可以這樣調用
await folder.GetOrCreateFileAsync("FileName");
拓展方法在C#的發展中有著舉足輕重的作用,System.Linq
就使用了大量拓展方法極大簡化了數據查詢:
//篩選最高溫大於30°C的每日天氣數據並按照天氣類型分組
var result = forecasts.Where(p => p.MaxTemperature > 30).GroupBy(p => p.WeatherType);
而現在,可拓展的內容不再局限於方法
我們可以拓展屬性、索引器(有參屬性)、靜態成員甚至運算符等內容
註意:
extensions
功能尚未正式進入C#13的預覽版,以下示例根據語言提案/Build 2024演示中的相關內容編寫,正式版語法可能有所不同目前的Roslyn實現在feature/roles分支,可以自行編譯嘗試
示例1:隱式拓展
假定有以下類型
public class DailyWeather
{
public int MaxTemperature { get; set; }
public int MinTemperature { get; set; }
public string WeatherType { get; set; }
public List<HourlyWeather> HourlyForecasts { get; set; }
public class HourlyWeather
{
public int Temperature { get; set; }
public string WeatherType { get; set; }
}
}
定義以下隱式拓展(implicit extension
)
拓展的語法與類十分相似,它可以訪問該類中的任意非private
或protected
成員,但不能有實例欄位
public implicit extension DailyWeatherExtension for DailyWeather
{
//拓展屬性:平均溫度
public int AverageTemperature => (int)Math.Round(HourlyForecasts.Average(p => p.Temperature));
//拓展索引器:獲取/修改某小時預報
public HourlyWeather this[int index]
{
get => HourlyForecasts[index];
set => HourlyForecasts[index] = value;
}
//拓展運算符:通過比較最高溫大小支持">"/"<"運算符
public static bool operator >(DailyWeather a,DailyWeather b)
{
return a.MaxTemperature > b.MaxTemperature;
}
public static bool operator <(DailyWeather a, DailyWeather b)
{
return a.MaxTemperature < b.MaxTemperature;
}
//拓展靜態方法:獲取今日天氣
public static async Task<DailyWeather> GetWeatherToday()
{
//從外部獲取今日天氣...
}
}
在代碼中,我們就可以這樣使用:
public async void PrintInfo(DailyWeather weather)
{
Console.WriteLine(weather.AverageTemperature);
var weatherToday = await DailyWeather.GetWeatherToday();
if(weather > weatherToday)
{
Console.WriteLine(weather[0].WeatherType);
}
}
這些“拓展”似乎就是類中真實存在的成員!原先需要繼承才能部分實現的功能,使用一個extension
即可完美解決
示例2:顯式拓展
通過聲明一個顯式拓展(explict extension
),我們可以使用類型轉換將類型轉換為拓展的類型並獲得相應的成員
有如下天氣數據JSON
{
"type": "clear",
"tempMax": 32,
"tempMin": 20,
"hourly": [
{
"time": "2024-06-09T00:00",
"type": "cloudy",
"temp": 20
},
{
"time": "2024-06-09T06:00",
"type": "clear",
"temp": 24
},
{
"time": "2024-06-09T12:00",
"type": "clear",
"temp": 32
},
{
"time": "2024-06-09T18:00",
"type": "cloudy",
"temp": 26
},
{
"time": "2024-06-09T23:00",
"type": "rain",
"temp": 21
}
]
}
定義如下顯式拓展:
explicit extension DailyWeather for JsonElement
{
public string WeatherType => this.GetProperty("type").GetString()!;
public string MaxTemperature => this.GetProperty("tempMax").GetString()!;
public string MinTemperature => this.GetProperty("tempMin").GetString()!;
public IEnumerable<HourlyWeather> HourlyForecasts => this.GetProperty("hourly")!.EnumerateArray();//此處有隱式類型轉換
}
explicit extension HourlyWeather for JsonElement
{
public DateTime Time => this.GetProperty("time").GetDateTime()!;
public int Temperature => this.GetProperty("temp").GetInt32()!;
public string WeatherType => this.GetProperty("type").GetString()!;
}
現在,我們可以用類型安全的方式訪問JSON中的內容
var data = jsonData.ParseAsJson();
var weather = (DailyWeather)data;
Console.WriteLine($"今日天氣:{weather.WeatherType}");
foreach(HourlyWeather hourly in weather.HourlyForecasts)//此處有隱式類型轉換
{
Console.WriteLine($"{hourly.Time.Hour}時的天氣:{hourly.WeatherType}");
}