共用方式為


擷取 .NET 應用程式中的資源

當您在 .NET 應用程式中使用本地化的資源時,最好使用主要元件封裝預設或中性文化特性的資源,併為應用程式支援的每個語言或文化特性建立個別的附屬元件。 接著,您可以如下一節所述使用ResourceManager類別來存取具名資源。 如果您選擇不要將資源內嵌在主要元件和附屬元件中,您也可以直接存取二進位 .resources 檔案,如本文稍後 從 .resources 檔案 擷取資源一節所述。

從元件擷取資源

類別 ResourceManager 可在運行時間存取資源。 您可以使用 ResourceManager.GetString 方法來擷取字串資源和 ResourceManager.GetObjectResourceManager.GetStream 方法來擷取非字串資源。 每個方法都有兩個重載。

資源管理員會使用資源後援程式來控制應用程式如何擷取特定文化特性的資源。 如需詳細資訊,請參閱「資源回退過程」章節中的套件和部署資源。 如需具現化 對象的相關信息,請參閱類別主題中的 <具現化 ResourceManager 物件>一節。

擷取字串數據範例

下列範例會呼叫 GetString(String) 方法來擷取目前UI文化特性的字串資源。 它包含英文(美國)文化特性的中性字串資源,以及法文(法國)和俄羅斯(俄羅斯)文化特性的當地語系化資源。 下列英文 (美國) 資源位於名為 Strings.txt的檔案中:

TimeHeader=The current time is

法文 (法國) 資源位於名為 Strings.fr-FR.txt的檔案中:

TimeHeader=L'heure actuelle est

俄羅斯(俄羅斯)資源位於名為 Strings.ru-RU.txt的檔案中:

TimeHeader=Текущее время —

此範例的原始碼位於 C# 版本的程式代碼名為 GetString.cs 的檔案中,Visual Basic 版本的GetString.vb,定義包含四個文化特性名稱的字串數位數組:資源可供使用的三種文化特性,以及西班牙文(西班牙)文化特性。 執行五次的迴圈會隨機選取其中一種文化特性,並將它指派給 Thread.CurrentCultureCultureInfo.CurrentUICulture 屬性。 然後,它會呼叫 GetString(String) 方法來擷取本地化的字串,其與一天中的時間一起顯示。

using System;
using System.Globalization;
using System.Resources;
using System.Threading;

[assembly: NeutralResourcesLanguageAttribute("en-US")]

public class Example
{
   public static void Main()
   {
      string[] cultureNames = { "en-US", "fr-FR", "ru-RU", "es-ES" };
      Random rnd = new Random();
      ResourceManager rm = new ResourceManager("Strings",
                               typeof(Example).Assembly);

      for (int ctr = 0; ctr <= cultureNames.Length; ctr++) {
         string cultureName = cultureNames[rnd.Next(0, cultureNames.Length)];
         CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName);
         Thread.CurrentThread.CurrentCulture = culture;
         Thread.CurrentThread.CurrentUICulture = culture;

         Console.WriteLine($"Current culture: {culture.NativeName}");
         string timeString = rm.GetString("TimeHeader");
         Console.WriteLine($"{timeString} {DateTime.Now:T}\n");
      }
   }
}
// The example displays output like the following:
//    Current culture: English (United States)
//    The current time is 9:34:18 AM
//
//    Current culture: Español (España, alfabetización internacional)
//    The current time is 9:34:18
//
//    Current culture: русский (Россия)
//    Текущее время — 9:34:18
//
//    Current culture: français (France)
//    L'heure actuelle est 09:34:18
//
//    Current culture: русский (Россия)
//    Текущее время — 9:34:18
Imports System.Globalization
Imports System.Resources
Imports System.Threading

<Assembly: NeutralResourcesLanguageAttribute("en-US")>

Module Example
    Public Sub Main()
        Dim cultureNames() As String = {"en-US", "fr-FR", "ru-RU", "es-ES"}
        Dim rnd As New Random()
        Dim rm As New ResourceManager("Strings", GetType(Example).Assembly)

        For ctr As Integer = 0 To cultureNames.Length
            Dim cultureName As String = cultureNames(rnd.Next(0, cultureNames.Length))
            Dim culture As CultureInfo = CultureInfo.CreateSpecificCulture(cultureName)
            Thread.CurrentThread.CurrentCulture = culture
            Thread.CurrentThread.CurrentUICulture = culture

            Console.WriteLine("Current culture: {0}", culture.NativeName)
            Dim timeString As String = rm.GetString("TimeHeader")
            Console.WriteLine("{0} {1:T}", timeString, Date.Now)
            Console.WriteLine()
        Next
    End Sub
End Module
' The example displays output similar to the following:
'    Current culture: English (United States)
'    The current time is 9:34:18 AM
'    
'    Current culture: Español (España, alfabetización internacional)
'    The current time is 9:34:18
'    
'    Current culture: русский (Россия)
'    Текущее время — 9:34:18
'    
'    Current culture: français (France)
'    L'heure actuelle est 09:34:18
'    
'    Current culture: русский (Россия)
'    Текущее время — 9:34:18

以下批次檔案 (.bat) 會編譯範例,並在適當的目錄中生成衍生組件。 以下命令是針對 C# 語言及其編譯器所提供的。 針對 Visual Basic,將變更 cscvbc,並將 變更 GetString.csGetString.vb

resgen strings.txt
csc GetString.cs -resource:strings.resources

resgen strings.fr-FR.txt
md fr-FR
al -embed:strings.fr-FR.resources -culture:fr-FR -out:fr-FR\GetString.resources.dll

resgen strings.ru-RU.txt
md ru-RU
al -embed:strings.ru-RU.resources -culture:ru-RU -out:ru-RU\GetString.resources.dll

當目前的UI文化特性是西班牙文(西班牙)時,請注意此範例會顯示英文語言資源,因為無法使用西班牙文語言資源,而英文是範例的預設文化特性。

擷取對象數據範例

您可以使用 GetObjectGetStream 方法來擷取對象數據。 這包括基本數據類型、可串行化的物件,以及以二進位格式儲存的物件(例如影像)。

下列範例使用 GetStream(String) 方法來擷取用於應用程式開啟畫面中啟動視窗的點陣圖。 名為 CreateResources.cs 或 CreateResources.vb 的檔案中的下列原始碼會產生包含串行化映像的 .resx 檔案。 在此情況下,映像會從名為 SplashScreen.jpg的檔案載入;您可以修改檔案名來取代您自己的映像。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Resources;

public class Example
{
   public static void Main()
   {
      Bitmap bmp = new Bitmap(@".\SplashScreen.jpg");
      MemoryStream imageStream = new MemoryStream();
      bmp.Save(imageStream, ImageFormat.Jpeg);

      ResXResourceWriter writer = new ResXResourceWriter("AppResources.resx");
      writer.AddResource("SplashScreen", imageStream);
      writer.Generate();
      writer.Close();
   }
}
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Resources

Module Example
    Public Sub Main()
        Dim bmp As New Bitmap(".\SplashScreen.jpg")
        Dim imageStream As New MemoryStream()
        bmp.Save(imageStream, ImageFormat.Jpeg)

        Dim writer As New ResXResourceWriter("AppResources.resx")
        writer.AddResource("SplashScreen", imageStream)
        writer.Generate()
        writer.Close()
    End Sub
End Module

下列程式代碼會擷取資源,並在控件中 PictureBox 顯示影像。

using System;
using System.Drawing;
using System.IO;
using System.Resources;
using System.Windows.Forms;

public class Example
{
   public static void Main()
   {
      ResourceManager rm = new ResourceManager("AppResources", typeof(Example).Assembly);
      Bitmap screen = (Bitmap) Image.FromStream(rm.GetStream("SplashScreen"));

      Form frm = new Form();
      frm.Size = new Size(300, 300);

      PictureBox pic = new PictureBox();
      pic.Bounds = frm.RestoreBounds;
      pic.BorderStyle = BorderStyle.Fixed3D;
      pic.Image = screen;
      pic.SizeMode = PictureBoxSizeMode.StretchImage;

      frm.Controls.Add(pic);
      pic.Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
                   AnchorStyles.Left | AnchorStyles.Right;

      frm.ShowDialog();
   }
}
Imports System.Drawing
Imports System.IO
Imports System.Resources
Imports System.Windows.Forms

Module Example
    Public Sub Main()
        Dim rm As New ResourceManager("AppResources", GetType(Example).Assembly)
        Dim screen As Bitmap = CType(Image.FromStream(rm.GetStream("SplashScreen")), Bitmap)

        Dim frm As New Form()
        frm.Size = new Size(300, 300)

        Dim pic As New PictureBox()
        pic.Bounds = frm.RestoreBounds
        pic.BorderStyle = BorderStyle.Fixed3D
        pic.Image = screen
        pic.SizeMode = PictureBoxSizeMode.StretchImage

        frm.Controls.Add(pic)
        pic.Anchor = AnchorStyles.Top Or AnchorStyles.Bottom Or
                     AnchorStyles.Left Or AnchorStyles.Right

        frm.ShowDialog()
    End Sub
End Module

您可以使用下列批次處理檔來建置 C# 範例。 針對 Visual Basic,將變更 cscvbc,並將原始碼檔案的延伸模組從 .cs 變更為 .vb

csc CreateResources.cs
CreateResources

resgen AppResources.resx

csc GetStream.cs -resource:AppResources.resources

下列範例會使用 ResourceManager.GetObject(String) 方法來還原序列化的自定義物件。 此範例包含名為 UIElements.cs 的原始程式碼檔案(UIElements.vb for Visual Basic),其定義名為 PersonTable的下列結構。 此結構是供顯示數據表數據行本地化名稱的一般數據表顯示例程使用。 請注意,結構 PersonTable 會以 SerializableAttribute 屬性標示。

using System;

[Serializable] public struct PersonTable
{
   public readonly int nColumns;
   public readonly string column1;
   public readonly string column2;
   public readonly string column3;
   public readonly int width1;
   public readonly int width2;
   public readonly int width3;

   public PersonTable(string column1, string column2, string column3,
                  int width1, int width2, int width3)
   {
      this.column1 = column1;
      this.column2 = column2;
      this.column3 = column3;
      this.width1 = width1;
      this.width2 = width2;
      this.width3 = width3;
      this.nColumns = typeof(PersonTable).GetFields().Length / 2;
   }
}
<Serializable> Public Structure PersonTable
    Public ReadOnly nColumns As Integer
    Public Readonly column1 As String
    Public ReadOnly column2 As String
    Public ReadOnly column3 As String
    Public ReadOnly width1 As Integer
    Public ReadOnly width2 As Integer
    Public ReadOnly width3 As Integer

    Public Sub New(column1 As String, column2 As String, column3 As String,
                   width1 As Integer, width2 As Integer, width3 As Integer)
        Me.column1 = column1
        Me.column2 = column2
        Me.column3 = column3
        Me.width1 = width1
        Me.width2 = width2
        Me.width3 = width3
        Me.nColumns = Me.GetType().GetFields().Count \ 2
    End Sub
End Structure

下列程式代碼來自名為 CreateResources.cs 的檔案 (CreateResources.vb for Visual Basic) 會建立名為 UIResources.resx 的 XML 資源檔,該檔案會儲存資料表標題,以及 PersonTable 包含針對英文當地語系化之應用程式的資訊的物件。

using System;
using System.Resources;

public class CreateResource
{
   public static void Main()
   {
      PersonTable table = new PersonTable("Name", "Employee Number",
                                          "Age", 30, 18, 5);
      ResXResourceWriter rr = new ResXResourceWriter(@".\UIResources.resx");
      rr.AddResource("TableName", "Employees of Acme Corporation");
      rr.AddResource("Employees", table);
      rr.Generate();
      rr.Close();
   }
}
Imports System.Resources

Module CreateResource
    Public Sub Main()
        Dim table As New PersonTable("Name", "Employee Number", "Age", 30, 18, 5)
        Dim rr As New ResXResourceWriter(".\UIResources.resx")
        rr.AddResource("TableName", "Employees of Acme Corporation")
        rr.AddResource("Employees", table)
        rr.Generate()
        rr.Close()
    End Sub
End Module

在名為 GetObject.cs (GetObject.vb) 的原始程式碼檔案中,下列程式代碼會擷取資源,並將其顯示至控制台。

using System;
using System.Resources;

[assembly: NeutralResourcesLanguageAttribute("en")]

public class Example
{
   public static void Main()
   {
      string fmtString = String.Empty;
      ResourceManager rm = new ResourceManager("UIResources", typeof(Example).Assembly);
      string title = rm.GetString("TableName");
      PersonTable tableInfo = (PersonTable) rm.GetObject("Employees");

      if (! String.IsNullOrEmpty(title)) {
         fmtString = "{0," + ((Console.WindowWidth + title.Length) / 2).ToString() + "}";
         Console.WriteLine(fmtString, title);
         Console.WriteLine();
      }

      for (int ctr = 1; ctr <= tableInfo.nColumns; ctr++) {
         string columnName = "column"  + ctr.ToString();
         string widthName = "width" + ctr.ToString();
         string value = tableInfo.GetType().GetField(columnName).GetValue(tableInfo).ToString();
         int width = (int) tableInfo.GetType().GetField(widthName).GetValue(tableInfo);
         fmtString = "{0,-" + width.ToString() + "}";
         Console.Write(fmtString, value);
      }
      Console.WriteLine();
   }
}
Imports System.Resources

<Assembly: NeutralResourcesLanguageAttribute("en")>

Module Example
    Public Sub Main()
        Dim fmtString As String = String.Empty
        Dim rm As New ResourceManager("UIResources", GetType(Example).Assembly)
        Dim title As String = rm.GetString("TableName")
        Dim tableInfo As PersonTable = DirectCast(rm.GetObject("Employees"), PersonTable)

        If Not String.IsNullOrEmpty(title) Then
            fmtString = "{0," + ((Console.WindowWidth + title.Length) \ 2).ToString() + "}"
            Console.WriteLine(fmtString, title)
            Console.WriteLine()
        End If

        For ctr As Integer = 1 To tableInfo.nColumns
            Dim columnName As String = "column" + ctr.ToString()
            Dim widthName As String = "width" + ctr.ToString()
            Dim value As String = CStr(tableInfo.GetType().GetField(columnName).GetValue(tableInfo))
            Dim width As Integer = CInt(tableInfo.GetType().GetField(widthName).GetValue(tableInfo))
            fmtString = "{0,-" + width.ToString() + "}"
            Console.Write(fmtString, value)
        Next
        Console.WriteLine()
    End Sub
End Module

您可以執行下列批處理檔來建置必要的資源檔和元件,並執行應用程式。 您必須使用 /r 選項來提供 Resgen.exe 與 UIElements.dll 的參考,這樣它就可以存取關於 PersonTable 結構的信息。 如果您使用 C#,請將 vbc 編譯程式名稱取代為 csc,並將延伸模組取代 .vb.cs

vbc -t:library UIElements.vb
vbc CreateResources.vb -r:UIElements.dll
CreateResources

resgen UIResources.resx  -r:UIElements.dll
vbc GetObject.vb -r:UIElements.dll -resource:UIResources.resources

GetObject.exe

衛星組件的版本支援

根據預設,當ResourceManager物件擷取所需的資源時,它會尋找那些版本號碼與主要元件版本號碼相符的附屬元件。 部署應用程式之後,您可能想要更新主要程式集或特定資源的衛星程式集。 .NET Framework 支援對主要元件和附屬元件進行版本控制。

屬性 SatelliteContractVersionAttribute 提供主要元件的版本控制支援。 在應用程式的主要元件上指定此屬性可讓您更新和重新部署主要元件,而不需更新其附屬元件。 更新主要元件之後,請遞增主要元件的版本號碼,但讓附屬合約版本號碼保持不變。 當資源管理員擷取要求的資源時,它會載入此屬性所指定的附屬元件版本。

發行者政策組件提供對版本化衛星組件的支援。 您可以更新和重新部署附屬元件,而不需更新主要元件。 更新附屬元件之後,請將版本號碼遞增,並搭配發行者原則元件一起發佈。 在發行者策略元件集,指定新的衛星元件與先前版本具備向後相容。 資源管理員會使用 SatelliteContractVersionAttribute 屬性來判斷附屬元件的版本,但元件載入器會系結至發行者原則所指定的附屬元件版本。 如需發行者原則元件的詳細資訊,請參閱 建立發行者原則檔案

若要啟用完整的元件版本控制支援,建議您在 全域程式集緩存 中部署強名稱元件,並在應用程式目錄中部署沒有強名稱的元件。 如果您想要在應用程式目錄中部署強名稱元件,當您更新元件時,將無法遞增附屬元件的版本號碼。 相反地,您必須執行就地更新,以更新的程式代碼取代現有的程式代碼,並維護相同的版本號碼。 例如,如果您要以完整指定的元件名稱「myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a」更新版本 1.0.0.0 的附屬元件,請使用已編譯的更新 myApp.resources.dll 覆寫它,上述更新應使用相同的完整指定元件名稱「myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a」。 請注意,對衛星組件檔案執行直接更新會使應用程式很難準確判斷衛星組件的版本。

如需元件版本設定的詳細資訊,請參閱 元件版本設定運行時間如何找出元件

從 .resources 檔案擷取資源

如果您選擇不要在附屬元件中部署資源,您仍然可以使用 ResourceManager 物件直接存取 .resources 檔案中的資源。 若要這樣做,您必須正確部署 .resources 檔案。 使用 ResourceManager.CreateFileBasedResourceManager 方法來實例化 ResourceManager 物件,並指定包含獨立 .resources 檔案的目錄。

部署 .resources 檔案

當您在應用程式元件和附屬元件中內嵌 .resources 檔案時,每個附屬元件都有相同的檔名,但會放在反映附屬元件文化特性的子目錄中。 相反地,當您直接從 .resources 檔案存取資源時,您可以將所有 .resources 檔案放在單一目錄中,通常是應用程式目錄的子目錄。 應用程式預設 .resources 檔案的名稱只包含根名稱,沒有其文化特性的指示(例如 strings.resources)。 每個本地化文化的資源都儲存在一個檔案中,檔案名稱由根名稱後面接著文化名稱組成(例如 strings.ja.resources 或 strings.de-DE.resources)。

下圖顯示資源檔案應該位於目錄結構的位置。 它也提供 .resource 檔案的命名慣例。

顯示應用程式主要目錄的圖例。

使用資源管理員

建立資源並將其放在適當的目錄中之後,您可以呼叫 ResourceManager 方法來建立 CreateFileBasedResourceManager(String, String, Type) 物件以使用資源。 第一個參數會指定應用程式預設 .resources 檔案的根名稱(上一節中的範例會是 “strings”。 第二個參數會指定資源的位置(前一個範例的「資源」)。 第三個參數指定要使用的實作。 如果第三個參數是 null,則會使用預設運行時間 ResourceSet

備註

請勿使用獨立 .resources 檔案部署 ASP.NET 應用程式。 這可能會導致鎖定問題並中斷 XCOPY 部署。 建議您在衛星組件中部署 ASP.NET 資源。 如需詳細資訊,請參閱 ASP.NET 網頁資源概觀

具現化 ResourceManager 對象之後,您可以使用 先前所討論的 GetStringGetObjectGetStream 方法來擷取資源。 不過,直接從 .resources 檔案擷取資源與從元件擷取內嵌資源不同。 當您從 .resources 檔案擷取資源時,不論目前的文化特性為何, GetString(String)GetObject(String)GetStream(String) 方法一律會擷取預設文化特性的資源。 若要擷取應用程式目前文化特性或特定文化特性的資源,您必須呼叫 GetString(String, CultureInfo)GetObject(String, CultureInfo)GetStream(String, CultureInfo) 方法,並指定要擷取其資源的文化特性。 若要擷取目前文化特性的資源,請將 屬性的值 CultureInfo.CurrentCulture 指定為 culture 自變數。 如果資源管理員無法擷取 culture 的資源,它會使用標準資源後援規則來擷取適當的資源。

範例

下列範例說明資源管理員如何直接從 .resources 檔案擷取資源。 此範例包含英文(美國)、法文(法國)和俄羅斯(俄羅斯)文化的三個文字型資源檔。 英文(美國)是範例的預設文化特性。 其資源會儲存在名為 Strings.txt的下列檔案中:

Greeting=Hello
Prompt=What is your name?

法國(法國)文化的資源儲存在下列名為 Strings.fr-FR.txt的檔案中:

Greeting=Bon jour
Prompt=Comment vous appelez-vous?

俄羅斯(俄羅斯)文化特性的資源會儲存在下列檔案中,名為 Strings.ru-RU.txt:

Greeting=Здравствуйте
Prompt=Как вас зовут?

以下是範例的原始程式碼。 此範例會具現化 CultureInfo 英文(美國)、英文(加拿大)、法文(法國)和俄文(俄羅斯)的文化物件,並將每個文化設為目前的文化。 ResourceManager.GetString(String, CultureInfo) 方法接著會提供 CultureInfo.CurrentCulture 屬性的值作為 culture 引數,以擷取特定文化相關的適當資源。

using System;
using System.Globalization;
using System.Resources;
using System.Threading;

[assembly: NeutralResourcesLanguage("en-US")]

public class Example
{
   public static void Main()
   {
      string[] cultureNames = { "en-US", "en-CA", "ru-RU", "fr-FR" };
      ResourceManager rm = ResourceManager.CreateFileBasedResourceManager("Strings", "Resources", null);

      foreach (var cultureName in cultureNames) {
         Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
         string greeting = rm.GetString("Greeting", CultureInfo.CurrentCulture);
         Console.WriteLine($"\n{greeting}!");
         Console.Write(rm.GetString("Prompt", CultureInfo.CurrentCulture));
         string name = Console.ReadLine();
         if (! String.IsNullOrEmpty(name))
            Console.WriteLine("{0}, {1}!", greeting, name);
      }
      Console.WriteLine();
   }
}
// The example displays output like the following:
//       Hello!
//       What is your name? Dakota
//       Hello, Dakota!
//
//       Hello!
//       What is your name? Koani
//       Hello, Koani!
//
//       Здравствуйте!
//       Как вас зовут?Samuel
//       Здравствуйте, Samuel!
//
//       Bon jour!
//       Comment vous appelez-vous?Yiska
//       Bon jour, Yiska!
Imports System.Globalization
Imports System.Resources
Imports System.Threading

<Assembly: NeutralResourcesLanguageAttribute("en-US")>

Module Example
    Public Sub Main()
        Dim cultureNames() As String = {"en-US", "en-CA", "ru-RU", "fr-FR"}
        Dim rm As ResourceManager = ResourceManager.CreateFileBasedResourceManager("Strings", "Resources", Nothing)

        For Each cultureName In cultureNames
            Console.WriteLine()
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName)
            Dim greeting As String = rm.GetString("Greeting", CultureInfo.CurrentCulture)
            Console.WriteLine("{0}!", greeting)
            Console.Write(rm.GetString("Prompt", CultureInfo.CurrentCulture))
            Dim name As String = Console.ReadLine()
            If Not String.IsNullOrEmpty(name) Then
                Console.WriteLine("{0}, {1}!", greeting, name)
            End If
        Next
        Console.WriteLine()
    End Sub
End Module
' The example displays output like the following:
'       Hello!
'       What is your name? Dakota
'       Hello, Dakota!
'       
'       Hello!
'       What is your name? Koani
'       Hello, Koani!
'       
'       Здравствуйте!
'       Как вас зовут?Samuel
'       Здравствуйте, Samuel!
'       
'       Bon jour!
'       Comment vous appelez-vous?Yiska
'       Bon jour, Yiska!

您可以執行下列批處理文件來編譯範例的 C# 版本。 如果您使用 Visual Basic,請將 取代 cscvbc,並將延伸模組取代 .cs.vb

md Resources
resgen Strings.txt Resources\Strings.resources
resgen Strings.fr-FR.txt Resources\Strings.fr-FR.resources
resgen Strings.ru-RU.txt Resources\Strings.ru-RU.resources

csc Example.cs

另請參閱