KGy SOFT

HybridResourceManager Class

KGy SOFT Core Libraries Help
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 Remarks section for an example and for the differences compared to ResourceManager class.
Inheritance Hierarchy

SystemObject
  System.ResourcesResourceManager
    KGySoft.ResourcesHybridResourceManager
      KGySoft.ResourcesDynamicResourceManager

Namespace:  KGySoft.Resources
Assembly:  KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 5.0.0-rc.1
Syntax

[SerializableAttribute]
public class HybridResourceManager : ResourceManager, 
	IExpandoResourceManager, IDisposable

The HybridResourceManager type exposes the following members.

Constructors

  NameDescription
Public methodHybridResourceManager(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.
Public methodHybridResourceManager(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.
Top
Properties

  NameDescription
Public propertyBaseName
Gets the root name of the resource files that the ResourceManager searches for resources.
(Inherited from ResourceManager.)
Public propertyCloneValues
Gets or sets whether GetObject and GetMetaObject methods return always a new copy of the stored values.
Default value: .
Protected propertyFallbackLocation
Gets or sets the location from which to retrieve default fallback resources.
(Inherited from ResourceManager.)
Public propertyIgnoreCase
Gets or sets a value that indicates whether the resource manager allows case-insensitive resource lookups in the GetString/GetMetaString and GetObject/GetMetaObject methods.
(Overrides ResourceManagerIgnoreCase.)
Public propertyIsDisposed
Gets whether this HybridResourceManager instance is disposed.
Public propertyIsModified
Gets whether this HybridResourceManager instance has modified and unsaved data.
Protected propertyNeutralResourcesCulture
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.
Public propertyResourceSetType
Gets the type of the resource set object that the HybridResourceManager uses to create a resource set for the binary resources. Please note that for .resx based resources other types can be created as well.
(Overrides ResourceManagerResourceSetType.)
Public propertyResXResourcesDir
Gets or sets the relative path to .resx resource files.
Default value: Resources
Public propertySafeMode
Gets or sets whether the HybridResourceManager works in safe mode. In safe mode the retrieved objects returned from .resx sources are not deserialized automatically.
See the Remarks section for details.
Default value: .
Public propertySource
Gets or sets the source, from which the resources should be taken.
Default value: CompiledAndResX
Public propertyThrowException
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: .
Top
Methods

  NameDescription
Public methodDispose
Disposes the resources of the current instance.
Protected methodDispose(Boolean)
Releases unmanaged and - optionally - managed resources.
Public methodEquals
Determines whether the specified object is equal to the current object.
(Inherited from Object.)
Protected methodFinalize
Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
(Inherited from Object.)
Public methodGetExpandoResourceSet
Retrieves the resource set for a particular culture, which can be dynamically modified.
Public methodGetHashCode
Serves as the default hash function.
(Inherited from Object.)
Public methodGetMetaObject
Returns the value of the specified non-string metadata for the specified culture.
Public methodGetMetaStream
Returns a MemoryStream instance from the metadata of the specified name and culture.
Public methodGetMetaString
Returns the value of the string metadata for the specified culture.
Public methodGetObject(String)
Returns the value of the specified resource.
(Overrides ResourceManagerGetObject(String).)
Public methodGetObject(String, CultureInfo)
Gets the value of the specified resource localized for the specified culture.
(Overrides ResourceManagerGetObject(String, CultureInfo).)
Protected methodGetResourceFileName
Generates the name of the resource file for the given CultureInfo object.
(Inherited from ResourceManager.)
Public methodGetResourceSet
Retrieves the resource set for a particular culture.
(Overrides ResourceManagerGetResourceSet(CultureInfo, Boolean, Boolean).)
Public methodGetStream(String)
Returns a MemoryStream instance from the resource of the specified name.
Public methodGetStream(String, CultureInfo)
Returns a MemoryStream instance from the resource of the specified name and culture.
Public methodGetString(String)
Returns the value of the specified string resource.
(Overrides ResourceManagerGetString(String).)
Public methodGetString(String, CultureInfo)
Returns the value of the string resource localized for the specified culture.
(Overrides ResourceManagerGetString(String, CultureInfo).)
Public methodGetType
Gets the Type of the current instance.
(Inherited from Object.)
Protected methodInternalGetResourceSet
Provides the implementation for finding a resource set.
(Overrides ResourceManagerInternalGetResourceSet(CultureInfo, Boolean, Boolean).)
Protected methodMemberwiseClone
Creates a shallow copy of the current Object.
(Inherited from Object.)
Public methodReleaseAllResources
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.)
Public methodRemoveMetaObject
Removes a metadata object from the current HybridResourceManager with the specified name for the specified culture.
Public methodRemoveObject
Removes a resource object from the current HybridResourceManager with the specified name for the specified culture.
Public methodSaveAllResources
Saves all already loaded resources.
Public methodSaveResourceSet
Saves the resource set of a particular culture if it has been already loaded.
Public methodSetMetaObject
Adds or replaces a metadata object in the current HybridResourceManager with the specified name for the specified culture.
Public methodSetObject
Adds or replaces a resource object in the current HybridResourceManager with the specified name for the specified culture.
Public methodToString
Returns a string that represents the current object.
(Inherited from Object.)
Top
Fields

  NameDescription
Protected fieldBaseNameField
Specifies the root name of the resource files that the ResourceManager searches for resources.
(Inherited from ResourceManager.)
Protected fieldMainAssembly
Specifies the main assembly that contains the resources.
(Inherited from ResourceManager.)
Protected fieldResourceSets Obsolete.
Contains a Hashtable that returns a mapping from cultures to ResourceSet objects.
(Inherited from ResourceManager.)
Top
Extension Methods

  NameDescription
Public Extension MethodConvert(Type, CultureInfo)Overloaded.
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.)
Public Extension MethodCode exampleConvertTTarget(CultureInfo)Overloaded.
Converts an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions.)
Public Extension MethodIn (Defined by ObjectExtensions.)
Public Extension MethodTryConvert(Type, Object)Overloaded.
Tries to convert an Object specified in the obj parameter to the desired targetType.
(Defined by ObjectExtensions.)
Public Extension MethodTryConvert(Type, CultureInfo, Object)Overloaded.
Tries to convert an Object specified in the obj parameter to the desired targetType.
(Defined by ObjectExtensions.)
Public Extension MethodTryConvertTTarget(TTarget)Overloaded.
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.)
Public Extension MethodTryConvertTTarget(CultureInfo, TTarget)Overloaded.
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.)
Top
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.

Tip Tip
To see when to use the ResXResourceReader, ResXResourceWriter, ResXResourceSet, ResXResourceManager, HybridResourceManager and DynamicResourceManager classes see the documentation of the KGySoft.Resources namespace.

Example: Expanding compiled resources created by Visual Studio

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 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 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 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:

  • If the SafeMode property is , and the result is from a .resx resource, then the return value of GetObject and GetMetaObject methods is a ResXDataNode rather than the resource or metadata value. This makes possible to check the raw .resx content before deserialization if the .resx file is from an untrusted source. The actual value can be obtained by the ResXDataNode.GetValue method. See also the third example at the ResXResourceSet class.
  • If the SafeMode property is , then GetString and GetMetaString methods will not throw an InvalidOperationException even for non-string entries. For non-string values the raw XML string value will be returned for resources from a .resx source and the result of the ToString method for resources from a compiled source.
  • If the SafeMode property is , then GetStream and GetMetaStream methods will not throw an InvalidOperationException. For values, which are neither MemoryStream nor byte[] instances, they return a stream wrapper for the same string value that is returned by the GetString/GetMetaString methods.
Security note 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 treat 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.

See Also

Reference