Usare i file con estensione resx a livello di codice

Nota

Questo articolo si applica a .NET Framework. Per informazioni che si applicano a .NET 5+ (incluso .NET Core), vedere Risorse nei file con estensione resx.

Poiché i file di risorse XML (con estensione resx) devono essere costituiti da codice XML ben definito, comprensivo di un'intestazione che deve seguire uno schema specifico, seguita da dati in coppie nome/valore, la creazione manuale di questi file è soggetta a errori. In alternativa, è possibile creare file con estensione resx a livello di codice usando tipi e membri inclusi nella libreria di classi .NET. La libreria di classi .NET può essere usata anche per recuperare risorse archiviate in file con estensione resx. Questo articolo illustra come usare i tipi e i membri nello spazio dei nomi System.Resources per i file RESX.

Questo articolo descrive solo l'uso di file XML (con estensione resx) che contengono risorse. Per informazioni sull'uso dei file di risorse binari incorporati negli assembly, vedere ResourceManager.

Avviso

Esistono anche modi per usare i file RESX, oltre all'uso a livello di codice. Quando si aggiunge un file di risorse a un progetto di Visual Studio, Visual Studio fornisce un'interfaccia per la creazione e la gestione di un file RESX e converte automaticamente il file RESX in un file con estensione resources in fase di compilazione. È anche possibile usare un editor di testo per modificare direttamente un file RESX. Prestare comunque attenzione a non modificare le eventuali informazioni binarie archiviate nel file, per evitare di danneggiarlo.

Creare un file con estensione resx

È possibile usare la classe System.Resources.ResXResourceWriter per creare un file RESX a livello di codice seguendo questa procedura:

  1. Creare un'istanza di un oggetto ResXResourceWriter chiamando il metodo ResXResourceWriter(String) e specificando il nome del file RESX. Il nome del file deve includere l'estensione resx. Se si crea un'istanza dell'oggetto ResXResourceWriter in un blocco using , non è necessario chiamare esplicitamente il metodo ResXResourceWriter.Close nel passaggio 3.

  2. Chiamare il metodo ResXResourceWriter.AddResource per ogni risorsa da aggiungere al file. Usare l'overload di questo metodo per aggiungere dati stringa, oggetto e binari (matrice di byte). Se la risorsa è un oggetto, deve essere serializzabile.

  3. Chiamare il metodo ResXResourceWriter.Close per generare il file di risorse e per rilasciare tutte le risorse. Se l'oggetto ResXResourceWriter è stato creato all'interno di un blocco using , le risorse vengono scritte nel file RESX e le risorse usate dall'oggetto ResXResourceWriter vengono rilasciate alla fine del blocco using .

Il file RESX risultante ha l'intestazione appropriata e un tag data per ogni risorsa aggiunta dal metodo ResXResourceWriter.AddResource .

Avviso

Non usare file di risorse per archiviare password, informazioni sensibili per la sicurezza o dati personali.

Nell'esempio seguente viene creato un file con estensione resx denominato CarResources.resx che archivia sei stringhe, un'icona e due oggetti definiti dall'applicazione, ovvero due oggetti Automobile . La classe Automobile, che viene definita e di cui viene creata un'istanza nell'esempio, è contrassegnata con l'attributo SerializableAttribute.

using System;
using System.Drawing;
using System.Resources;

[Serializable()] public class Automobile
{
   private string carMake;
   private string carModel;
   private int carYear;
   private int carDoors;
   private int carCylinders;

   public Automobile(string make, string model, int year) :
                     this(make, model, year, 0, 0)
   { }

   public Automobile(string make, string model, int year,
                     int doors, int cylinders)
   {
      this.carMake = make;
      this.carModel = model;
      this.carYear = year;
      this.carDoors = doors;
      this.carCylinders = cylinders;
   }

   public string Make {
      get { return this.carMake; }
   }

   public string Model {
      get {return this.carModel; }
   }

   public int Year {
      get { return this.carYear; }
   }

   public int Doors {
      get { return this.carDoors; }
   }

   public int Cylinders {
      get { return this.carCylinders; }
   }
}

public class Example
{
   public static void Main()
   {
      // Instantiate an Automobile object.
      Automobile car1 = new Automobile("Ford", "Model N", 1906, 0, 4);
      Automobile car2 = new Automobile("Ford", "Model T", 1909, 2, 4);
      // Define a resource file named CarResources.resx.
      using (ResXResourceWriter resx = new ResXResourceWriter(@".\CarResources.resx"))
      {
         resx.AddResource("Title", "Classic American Cars");
         resx.AddResource("HeaderString1", "Make");
         resx.AddResource("HeaderString2", "Model");
         resx.AddResource("HeaderString3", "Year");
         resx.AddResource("HeaderString4", "Doors");
         resx.AddResource("HeaderString5", "Cylinders");
         resx.AddResource("Information", SystemIcons.Information);
         resx.AddResource("EarlyAuto1", car1);
         resx.AddResource("EarlyAuto2", car2);
      }
   }
}
Imports System.Drawing
Imports System.Resources

<Serializable()> Public Class Automobile
    Private carMake As String
    Private carModel As String
    Private carYear As Integer
    Private carDoors AS Integer
    Private carCylinders As Integer

    Public Sub New(make As String, model As String, year As Integer)
        Me.New(make, model, year, 0, 0)
    End Sub

    Public Sub New(make As String, model As String, year As Integer,
                   doors As Integer, cylinders As Integer)
        Me.carMake = make
        Me.carModel = model
        Me.carYear = year
        Me.carDoors = doors
        Me.carCylinders = cylinders
    End Sub

    Public ReadOnly Property Make As String
        Get
            Return Me.carMake
        End Get
    End Property

    Public ReadOnly Property Model As String
        Get
            Return Me.carModel
        End Get
    End Property

    Public ReadOnly Property Year As Integer
        Get
            Return Me.carYear
        End Get
    End Property

    Public ReadOnly Property Doors As Integer
        Get
            Return Me.carDoors
        End Get
    End Property

    Public ReadOnly Property Cylinders As Integer
        Get
            Return Me.carCylinders
        End Get
    End Property
End Class

Module Example
    Public Sub Main()
        ' Instantiate an Automobile object.
        Dim car1 As New Automobile("Ford", "Model N", 1906, 0, 4)
        Dim car2 As New Automobile("Ford", "Model T", 1909, 2, 4)
        ' Define a resource file named CarResources.resx.
        Using resx As New ResXResourceWriter(".\CarResources.resx")
            resx.AddResource("Title", "Classic American Cars")
            resx.AddResource("HeaderString1", "Make")
            resx.AddResource("HeaderString2", "Model")
            resx.AddResource("HeaderString3", "Year")
            resx.AddResource("HeaderString4", "Doors")
            resx.AddResource("HeaderString5", "Cylinders")
            resx.AddResource("Information", SystemIcons.Information)
            resx.AddResource("EarlyAuto1", car1)
            resx.AddResource("EarlyAuto2", car2)
        End Using
    End Sub
End Module

Suggerimento

Si può anche usare Visual Studio per creare file RESX. In fase di compilazione Visual Studio usa il generatore di file di risorse (Resgen.exe) per convertire il file RESX in una risorsa binaria (con estensione resources) e lo incorpora anche nell'assembly dell'applicazione o in un assembly satellite.

Non è possibile incorporare un file RESX in un eseguibile di runtime o compilarlo in un assembly satellite. Il file RESX deve essere convertito in un file di risorse binario (con estensione resources) usando il generatore di file di risorse (Resgen.exe). Successivamente, il file con estensione resources risultante può essere incorporato in un assembly dell'applicazione o in un assembly satellite. Per altre informazioni, vedere Creare file di risorse.

Enumerare le risorse

In alcuni casi può essere necessario recuperare tutte le risorse anziché una risorsa specifica da un file RESX. A questo scopo, si può usare la classe System.Resources.ResXResourceReader , che fornisce un enumeratore per tutte le risorse nel file RESX. La classe System.Resources.ResXResourceReader implementa IDictionaryEnumerator, che restituisce un oggetto DictionaryEntry che rappresenta una risorsa specifica per ogni iterazione del ciclo. La relativa proprietà DictionaryEntry.Key restituisce la chiave della risorsa e la proprietà DictionaryEntry.Value restituisce il valore della risorsa.

L'esempio seguente consente di creare un oggetto ResXResourceReader per il file CarResources.resx creato nell'esempio precedente e di scorrere il file di risorse. Aggiunge i due oggetti Automobile definiti nel file di risorse a un oggetto System.Collections.Generic.List<T> e aggiunge cinque delle sei stringhe a un oggetto SortedList . I valori nell'oggetto SortedList vengono convertiti in una matrice di parametri, usata per visualizzare le intestazioni di colonna nella console. Nella console vengono visualizzati anche i valori della proprietà Automobile .

using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;

public class Example
{
   public static void Main()
   {
      string resxFile = @".\CarResources.resx";
      List<Automobile> autos = new List<Automobile>();
      SortedList headers = new SortedList();

      using (ResXResourceReader resxReader = new ResXResourceReader(resxFile))
      {
         foreach (DictionaryEntry entry in resxReader) {
            if (((string) entry.Key).StartsWith("EarlyAuto"))
               autos.Add((Automobile) entry.Value);
            else if (((string) entry.Key).StartsWith("Header"))
               headers.Add((string) entry.Key, (string) entry.Value);
         }
      }
      string[] headerColumns = new string[headers.Count];
      headers.GetValueList().CopyTo(headerColumns, 0);
      Console.WriteLine("{0,-8} {1,-10} {2,-4}   {3,-5}   {4,-9}\n",
                        headerColumns);
      foreach (var auto in autos)
         Console.WriteLine("{0,-8} {1,-10} {2,4}   {3,5}   {4,9}",
                           auto.Make, auto.Model, auto.Year,
                           auto.Doors, auto.Cylinders);
   }
}
// The example displays the following output:
//       Make     Model      Year   Doors   Cylinders
//
//       Ford     Model N    1906       0           4
//       Ford     Model T    1909       2           4
Imports System.Collections
Imports System.Collections.Generic
Imports System.Resources

Module Example
    Public Sub Main()
        Dim resxFile As String = ".\CarResources.resx"
        Dim autos As New List(Of Automobile)
        Dim headers As New SortedList()

        Using resxReader As New ResXResourceReader(resxFile)
            For Each entry As DictionaryEntry In resxReader
                If CType(entry.Key, String).StartsWith("EarlyAuto") Then
                    autos.Add(CType(entry.Value, Automobile))
                Else If CType(entry.Key, String).StartsWith("Header") Then
                    headers.Add(CType(entry.Key, String), CType(entry.Value, String))
                End If
            Next
        End Using
        Dim headerColumns(headers.Count - 1) As String
        headers.GetValueList().CopyTo(headerColumns, 0)
        Console.WriteLine("{0,-8} {1,-10} {2,-4}   {3,-5}   {4,-9}",
                          headerColumns)
        Console.WriteLine()
        For Each auto In autos
            Console.WriteLine("{0,-8} {1,-10} {2,4}   {3,5}   {4,9}",
                              auto.Make, auto.Model, auto.Year,
                              auto.Doors, auto.Cylinders)
        Next
    End Sub
End Module
' The example displays the following output:
'       Make     Model      Year   Doors   Cylinders
'       
'       Ford     Model N    1906       0           4
'       Ford     Model T    1909       2           4

Recuperare una risorsa specifica

Oltre a enumerare gli elementi in un file RESX, è possibile recuperare una risorsa specifica in base al nome tramite la classe System.Resources.ResXResourceSet . Il metodo ResourceSet.GetString(String) recupera il valore di una risorsa di tipo stringa denominata. Il metodo ResourceSet.GetObject(String) recupera il valore di un oggetto denominato o di dati binari. Il metodo restituisce un oggetto di cui deve essere eseguito il cast (in C#) o da convertire (in Visual Basic) in un oggetto del tipo appropriato.

L'esempio seguente recupera l'icona e la stringa della didascalia di un form in base ai nomi delle risorse corrispondenti. Recupera anche gli oggetti Automobile definiti dall'applicazione usati nell'esempio precedente e li visualizza in un controllo DataGridView.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Resources;
using System.Windows.Forms;

public class CarDisplayApp : Form
{
   private const string resxFile = @".\CarResources.resx";
   Automobile[] cars;

   public static void Main()
   {
      CarDisplayApp app = new CarDisplayApp();
      Application.Run(app);
   }

   public CarDisplayApp()
   {
      // Instantiate controls.
      PictureBox pictureBox = new PictureBox();
      pictureBox.Location = new Point(10, 10);
      this.Controls.Add(pictureBox);
      DataGridView grid = new DataGridView();
      grid.Location = new Point(10, 60);
      this.Controls.Add(grid);

      // Get resources from .resx file.
      using (ResXResourceSet resxSet = new ResXResourceSet(resxFile))
      {
         // Retrieve the string resource for the title.
         this.Text = resxSet.GetString("Title");
         // Retrieve the image.
         Icon image = (Icon) resxSet.GetObject("Information", true);
         if (image != null)
            pictureBox.Image = image.ToBitmap();

         // Retrieve Automobile objects.
         List<Automobile> carList = new List<Automobile>();
         string resName = "EarlyAuto";
         Automobile auto;
         int ctr = 1;
         do {
            auto = (Automobile) resxSet.GetObject(resName + ctr.ToString());
            ctr++;
            if (auto != null)
               carList.Add(auto);
         } while (auto != null);
         cars = carList.ToArray();
         grid.DataSource = cars;
      }
   }
}
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Resources
Imports System.Windows.Forms

Public Class CarDisplayApp : Inherits Form
    Private Const resxFile As String = ".\CarResources.resx"
    Dim cars() As Automobile

    Public Shared Sub Main()
        Dim app As New CarDisplayApp()
        Application.Run(app)
    End Sub

    Public Sub New()
        ' Instantiate controls.
        Dim pictureBox As New PictureBox()
        pictureBox.Location = New Point(10, 10)
        Me.Controls.Add(pictureBox)
        Dim grid As New DataGridView()
        grid.Location = New Point(10, 60)
        Me.Controls.Add(grid)

        ' Get resources from .resx file.
        Using resxSet As New ResXResourceSet(resxFile)
            ' Retrieve the string resource for the title.
            Me.Text = resxSet.GetString("Title")
            ' Retrieve the image.
            Dim image As Icon = CType(resxSet.GetObject("Information", True), Icon)
            If image IsNot Nothing Then
                pictureBox.Image = image.ToBitmap()
            End If

            ' Retrieve Automobile objects.  
            Dim carList As New List(Of Automobile)
            Dim resName As String = "EarlyAuto"
            Dim auto As Automobile
            Dim ctr As Integer = 1
            Do
                auto = CType(resxSet.GetObject(resName + ctr.ToString()), Automobile)
                ctr += 1
                If auto IsNot Nothing Then carList.Add(auto)
            Loop While auto IsNot Nothing
            cars = carList.ToArray()
            grid.DataSource = cars
        End Using
    End Sub
End Class

Convertire i file RESX in file binari con estensione resources

La conversione dei file RESX in file di risorse binari incorporati (con estensione resources) presenta vantaggi significativi. Sebbene i file RESX siano facili da leggere e gestire durante lo sviluppo di applicazioni, è raro che siano inclusi nelle applicazioni finite. Se vengono distribuiti con un'applicazione, sono costituiti da file separati indipendenti dall'eseguibile dell'applicazione e dalle librerie associate. Al contrario, i file con estensione resources sono incorporati nell'eseguibile dell'applicazione o negli assembly associati. Inoltre, per le applicazioni localizzate, se si basano su file RESX in fase di esecuzione la responsabilità della gestione del fallback delle risorse ricade sullo sviluppatore. Se invece è stato creato un set di assembly satellite che contengono i file con estensione resources incorporati, il processo di fallback delle risorse viene gestito da Common Language Runtime.

Per convertire un file con estensione resx in un file con estensione resources, usare il generatore di file di risorse (resgen.exe), con la seguente sintassi di base:

 resgen.exe .resxFilename

Il risultato è un file di risorse binario con lo stesso nome file radice del file RESX e l'estensione resources. Questo file può quindi essere compilato in un file eseguibile o una libreria in fase di compilazione. Se si usa il compilatore Visual Basic, usare la sintassi seguente per incorporare un file con estensione resources nell'eseguibile di un'applicazione:

vbc filename .vb -resource: .resourcesFilename

Se si usa C#, la sintassi è la seguente:

 csc filename .cs -resource: .resourcesFilename

Il file con estensione resources può anche essere incorporato in un assembly satellite tramite Assembly Linker (AL.exe), che ha la seguente sintassi di base:

al resourcesFilename -out: assemblyFilename

Vedi anche