HybridResourceManager Class

Represents a resource manager that provides convenient access to culture-specific resources at run time. It can handle both compiled resources from .dll and .exe files, and .resx files at the same time. New elements can be added as well, which can be saved into .resx files.

See the online help for examples with images.

Definition

Namespace: KGySoft.Resources
Assembly: KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 9.0.0
C#
[SerializableAttribute]
public class HybridResourceManager : ResourceManager, 
	IExpandoResourceManager, IDisposable
Inheritance
Object    ResourceManager    HybridResourceManager
Derived
Implements
IExpandoResourceManager, IDisposable

Remarks

HybridResourceManager class is derived from ResourceManager and uses a ResXResourceManager internally. The HybridResourceManager combines the functionality of the regular ResourceManager and the ResXResourceManager classes. The source of the resources can be chosen by the Source property (see ResourceManagerSources enumeration). Enabling both binary and .resx resources makes possible to expand or override the resources originally come from binary resources. Just like the ResXResourceManager it is an IExpandoResourceManager implementation. The replacement and newly added content can be saved into .resx files.

See the Comparison with ResourceManager section to see all of the differences.

Examples

You can create compiled resources by Visual Studio and you can dynamically expand them by HybridResourceManager. The new and overridden content will be saved as .resx files. See the following example for a step-by-step guide.

  1. Create a new project (Console Application)
    New console application
  2. In Solution Explorer right click on ConsoleApp1, Add, New Folder, name it Resources.
  3. In Solution Explorer right click on Resources, Add, New Item, Resources File.
    New Resources file
  4. In Solution Explorer right click on the new resource file (Resource1.resx if not named otherwise) and select Properties
  5. The default value of Build Action is Embedded Resource, which means that the resource will be compiled into the assembly. The HybridResourceManager will be able to read these compiled resources if its Source property is CompiledOnly or CompiledAndResX. We can clear the default Custom Tool value because the generated file uses a ResourceManager class internally, which cannot handle the dynamic expansions.
    Resources1.resx properties

      Tip

    To use purely the .resx files you can use the ResXResourceManager class. To provide dynamic creation and expansion of new .resx files with the untranslated items for any language use the DynamicResourceManager class.
  6. Now we can either use the built-on resource editor of Visual Studio or just edit the .resx file by the XML Editor. If we add new or existing files to the resources, they will be automatically added to the project's Resources folder.
  7. To add culture-specific resources you can add further resource files with the same base name, extended by culture names. For example, if the invariant resource is called Resource1.resx, then a region neutral English resource can be called Resource1.en.resx and the American English resource can be called Resource1.en-US.resx.
  8. If we now compile the project, the ConsoleApp1.exe will contain an embedded resource named ConsoleApp1.Resources.Resource1.resources. If we created language-specific resources they will be compiled into so-called satellite assemblies. For example, if we have a resource file named Resource1.en.resx, then building the project will create an en folder containing a ConsoleApp1.resources.dll, which will contain an embedded resource named ConsoleApp1.Resources.Resource1.en.resources. In both cases the base name is ConsoleApp1.Resources.Resource1, this must be the baseName parameter in the constructors of the HybridResourceManager.

      Tip

    The automatically generated base name can be changed in the .csproj file. If you want to change the base name to MyResources, then follow the steps below:
    1. In Solution Explorer right click on ConsoleApp1, Unload Project
    2. In Solution Explorer right click on ConsoleApp1 (unavailable), Edit ConsoleApp1.csproj
    3. Search for the EmbeddedResource nodes and edit them as follows:
      XML
      <EmbeddedResource Include="Resources\Resource1.resx" >
        <LogicalName>MyResources.resources</LogicalName>
      </EmbeddedResource>
      <EmbeddedResource Include="Resources\Resource1.en.resx" >
        <LogicalName>MyResources.en.resources</LogicalName>
      </EmbeddedResource>
    4. In Solution Explorer right click on ConsoleApp1 (unavailable), Reload ConsoleApp1.csproj
  9. Reference KGySoft.CoreLibraries.dll and paste the following code in Program.cs:

C#
using System;
using System.Globalization;
using KGySoft.Resources;

public class Program
{
    public static void Main()
    {
        var enUS = CultureInfo.GetCultureInfo("en-US");
        var en = enUS.Parent;
        var inv = en.Parent; // same as CultureInfo.InvariantCulture

        var resourceManager = new HybridResourceManager(
                baseName: "ConsoleApp1.Resources.Resource1", // Or "MyResources" if you followed the tip above.
                assembly: typeof(Program).Assembly, // This is the assembly contains the compiled resources
                explicitResXBaseFileName: null) // (optional) if not null, a different name can be specified from baseName
            {
                ResXResourcesDir = "Resources", // The subfolder of .resx resources. Default is "Resources"
                Source = ResourceManagerSources.CompiledAndResX // Both types of resources are used. Default is CompiledAndResX
            };

        // If no .resx file exists yet the results will come purely from compiled resources
        Console.WriteLine("Results before adding some content:");
        Console.WriteLine(resourceManager.GetString("String1", enUS)); // from en because en-US does not exist
        Console.WriteLine(resourceManager.GetString("String1", en)); // from en
        Console.WriteLine(resourceManager.GetString("String1", inv)); // from invariant

        // Adding some new content
        resourceManager.SetObject("String1", "Replaced content in invariant culture", inv); // overriding existing compiled entry
        resourceManager.SetObject("NewObject", 42, inv); // completely newly defined entry
        resourceManager.SetObject("String1", "Test string in lately added American English resource file", enUS);

        Console.WriteLine();
        Console.WriteLine("Results after adding some content:");
        Console.WriteLine(resourceManager.GetString("String1", enUS)); // from en-US resx
        Console.WriteLine(resourceManager.GetString("String1", en)); // from compiled en
        Console.WriteLine(resourceManager.GetString("String1", inv)); // from invariant .resx

        // Removing works for .resx content. Removing an overridden entry resets the original compiled content.
        resourceManager.RemoveObject("String1", inv);

        // But by setting null explicitly even a compiled value can be suppressed.
        // By removing the null entry the compiled content will re-appear
        resourceManager.SetObject("String1", null, en);

        Console.WriteLine();
        Console.WriteLine("Results after deleting some content:");
        Console.WriteLine(resourceManager.GetString("String1", enUS)); // from en-US .resx
        Console.WriteLine(resourceManager.GetString("String1", en)); // from invariant because en is overridden by null
        Console.WriteLine(resourceManager.GetString("String1", inv)); // from original invariant because .resx is removed

        // By saving the resources the changes above can be persisted
        // resourceManager.SaveAllResources();
    }
}

// A possible result of the example above (depending on the created resource files and the added content)
// Results before adding some content:
// Test string in compiled en resource
// Test string in compiled en resource
// Test string in compiled invariant resource
//
// Results after adding some content:
// Test string in lately added American English resource file
// Test string in compiled en resource
// Replaced content in invariant culture
// 
// Results after deleting some content:
// Test string in lately added American English resource file
// Test string in compiled invariant resource
// Test string in compiled invariant resource

Considering there are .resx files in the background not just String and other Object resources can be obtained by GetString and GetObject methods but metadata as well by GetMetaString and GetMetaObject methods. Please note that accessing aliases are not exposed by the HybridResourceManager class, but you can still access them via the IExpandoResourceSet type returned by the GetExpandoResourceSet method.

  Note

Please note that unlike in case of GetString and GetObject methods, there is no falling back to the parent cultures (as seen in the example above) for metadata accessed by the GetMetaString and GetMetaObject methods.

Safety 

Similarly to ResXResourceSet, ResXResourceReader and ResXResourceManager, the HybridResourceManager class also has a SafeMode property, which changes the behavior of GetString/GetMetaString and GetObject/GetMetaObject methods:

  Security Note

Even if SafeMode is , loading a .resx content with corrupt or malicious entry will have no effect until we try to obtain the corresponding value. See the last example at ResXResourceSet for the demonstration and the example at ResXDataNode to see what members can be checked in safe mode.

Comparison with ResourceManager 

While ResourceManager is read-only and works on binary resources, HybridResourceManager supports expansion (see IExpandoResourceManager) and works both on binary and XML resources (.resx files).

Incompatibility with ResourceManager:

  • There is no constructor where the type of the resource sets can be specified. As HybridResourceManager works with multiple resource types you must not rely on the value of the ResourceSetType property.
  • If ResourceManager.GetResourceSet method is called with createIfNotExists = false for a culture, which has a corresponding but not loaded resource file, then a resource set for a parent culture might be cached and on successive calls that cached parent set will be returned even if the createIfNotExists argument is . In HybridResourceManager the corresponding argument of the GetResourceSet method has been renamed to loadIfExists and works as expected.
  • The GetStream methods have MemoryStream return type instead of UnmanagedMemoryStream and they can be used also for byte[] values.

New features and improvements compared to ResourceManager:

  • Write support – The stored content can be expanded or existing entries can be replaced (see SetObject/SetMetaObject), the entries can be removed (see RemoveObject/RemoveMetaObject), and the new content can be saved (see SaveAllResources/SaveResourceSet). You can start even with pure compiled resources, add new content during runtime and save the changes (see the example above).
  • Security – During the initialization of HybridResourceManager and loading of a resource set from a .resx file no object is deserialized even if SafeMode property is . Objects are deserialized only when they are accessed (see GetObject/GetMetaObject). If SafeMode is , then security is even more increased because the GetObject and GetMetaObject methods return a ResXDataNode instance instead of a deserialized object so you can check whether the resource or metadata can be treated as a safe object before actually deserializing it. See the Safety section above for more details.
  • Disposal – As ResourceSet implementations are disposable objects, HybridResourceManager itself implements the IDisposable interface as well.

Constructors

HybridResourceManager(Type, String) Creates a new instance of HybridResourceManager class that looks up resources in compiled assemblies and resource XML files based on information from the specified type object.
HybridResourceManager(String, Assembly, String) Creates a new instance of HybridResourceManager class that looks up resources in compiled assemblies and resource XML files based on information from the specified baseName and assembly.

Properties

CloneValues Gets or sets whether GetObject and GetMetaObject methods return always a new copy of the stored values.
Default value: .
IgnoreCase Gets or sets a value that indicates whether the resource manager allows case-insensitive resource lookups in the GetString/GetMetaString and GetObject/GetMetaObject methods.
Default value: .
(Overrides ResourceManagerIgnoreCase)
IgnoreResXParseErrors Gets or sets whether .resx file errors should be ignored when attempting to load a resource set. If , then non-loadable resource sets are considered as missing ones; otherwise, an exception is thrown.
Default value: .
IsDisposed Gets whether this HybridResourceManager instance is disposed.
IsModified Gets whether this HybridResourceManager instance has modified and unsaved data.
NeutralResourcesCulture Gets the CultureInfo that is specified as neutral culture in the Assembly used to initialized this instance, or the CultureInfo.InvariantCulture if no such culture is defined.
ResXResourcesDir Gets or sets the relative path to .resx resource files.
Default value: Resources
SafeMode Gets or sets whether the HybridResourceManager works in safe mode. In safe mode the retrieved objects returned from .resx sources are not deserialized automatically.
Default value: .
Source Gets or sets the source, from which the resources should be taken.
Default value: CompiledAndResX
ThrowException Gets or sets whether a MissingManifestResourceException should be thrown when a resource .resx file or compiled manifest is not found even for the neutral culture.
Default value: .

Methods

Dispose Disposes the resources of the current instance.
Dispose(Boolean) Releases the resources used by this HybridResourceManager instance.
GetExpandoResourceSet Retrieves the resource set for a particular culture, which can be dynamically modified.
GetMetaObject Returns the value of the specified non-string metadata for the specified culture.
GetMetaStream Returns a MemoryStream instance from the metadata of the specified name and culture.
GetMetaString Returns the value of the string metadata for the specified culture.
GetObject(String) Returns the value of the specified resource.
(Overrides ResourceManagerGetObject(String))
GetObject(String, CultureInfo) Gets the value of the specified resource localized for the specified culture.
(Overrides ResourceManagerGetObject(String, CultureInfo))
GetResourceSet Retrieves the resource set for a particular culture.
(Overrides ResourceManagerGetResourceSet(CultureInfo, Boolean, Boolean))
GetStream(String) Returns a MemoryStream instance from the resource of the specified name.
GetStream(String, CultureInfo) Returns a MemoryStream instance from the resource of the specified name and culture.
GetString(String) Returns the value of the specified string resource.
(Overrides ResourceManagerGetString(String))
GetString(String, CultureInfo) Returns the value of the string resource localized for the specified culture.
(Overrides ResourceManagerGetString(String, CultureInfo))
InternalGetResourceSet Provides the implementation for finding a resource set.
(Overrides ResourceManagerInternalGetResourceSet(CultureInfo, Boolean, Boolean))
ReleaseAllResources Tells the resource manager to call the ResourceSet.Close method on all ResourceSet objects and release all resources. All unsaved resources will be lost.
(Overrides ResourceManagerReleaseAllResources)
RemoveMetaObject Removes a metadata object from the current HybridResourceManager with the specified name for the specified culture.
RemoveObject Removes a resource object from the current HybridResourceManager with the specified name for the specified culture.
SaveAllResources Saves all already loaded resources.
SaveResourceSet Saves the resource set of a particular culture if it has been already loaded.
SetMetaObject Adds or replaces a metadata object in the current HybridResourceManager with the specified name for the specified culture.
SetObject Adds or replaces a resource object in the current HybridResourceManager with the specified name for the specified culture.

Extension Methods

Convert Converts an Object specified in the obj parameter to the desired targetType.
See the Examples section of the generic ConvertTTarget(Object, CultureInfo) overload for an example.
(Defined by ObjectExtensions)
ConvertTTarget Converts an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions)
In Gets whether item is among the elements of set.
See the Examples section of the generic InT(T, T) overload for an example.
(Defined by ObjectExtensions)
TryConvert Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvert Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvertTTarget Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvertTTarget Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)

See Also