/// <summary> /// Aggressively searches to find a resource based on a <see cref="Type"/> and resource name. /// <para/> /// The search attempts to find the resource starting at the location of the current /// class and attempts every combination of starting namespace and or starting assembly name /// (split on <c>.</c>) concatenated with the <paramref name="name"/>. For example, if the /// type passed is in the namespace <c>Foo.Bar</c> in an assembly named <c>Faz.Baz</c> /// and the name passed is <c>res.txt</c>, the following locations are searched in this order: /// <code> /// 1. res.txt /// 2. Faz.Baz.Foo.Bar.res.txt /// 3. Foo.Bar.res.txt /// 4. Faz.Baz.res.txt /// </code> /// <para/> /// Usage Note: This method effectively treats embedded resources as being in the same /// "class path" as the type that is passed, making it similar to the Class.getResource() /// method in Java. /// </summary> /// <param name="assembly">This assembly.</param> /// <param name="type">A type in the same namespace as the resource.</param> /// <param name="name">The resource name to locate.</param> /// <returns>The resource, if found; if not found, returns <c>null</c>.</returns> /// <exception cref="ArgumentNullException">If <paramref name="assembly"/>, <paramref name="type"/> or <paramref name="name"/> is <c>null</c>.</exception> public static string FindResource(this Assembly assembly, Type type, string name) { if (assembly == null) { throw new ArgumentNullException(nameof(assembly)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } if (name == null) { throw new ArgumentNullException(nameof(name)); } TypeAndResource key = new TypeAndResource(type, name); return(resourceCache.GetOrAdd(key, (key) => { string[] resourceNames = assembly.GetManifestResourceNames(); string resourceName = resourceNames.Where(x => x.Equals(name, StringComparison.Ordinal)).FirstOrDefault(); // If resourceName is not null, we have an exact match, don't search if (resourceName == null) { #if FEATURE_TYPEEXTENSIONS_GETTYPEINFO string assemblyName = type.GetTypeInfo().Assembly.GetName().Name; string namespaceName = type.GetTypeInfo().Namespace; #else string assemblyName = type.Assembly.GetName().Name; string namespaceName = type.Namespace; #endif // Search by assembly + namespace string resourceToFind = string.Concat(namespaceName, ".", name); if (!TryFindResource(resourceNames, assemblyName, resourceToFind, name, out resourceName)) { string found1 = resourceName; // Search by namespace only if (!TryFindResource(resourceNames, null, resourceToFind, name, out resourceName)) { string found2 = resourceName; // Search by assembly name only resourceToFind = string.Concat(assemblyName, ".", name); if (!TryFindResource(resourceNames, null, resourceToFind, name, out resourceName)) { // Take the first match of multiple, if there are any resourceName = found1 ?? found2 ?? resourceName; } } } } return resourceName; })); }
/// <summary> /// Aggressively searches to find a resource based on a resource name. /// Attempts to find the resource file by prepending the assembly name /// with the resource name and then removing the segements of the /// name from left to right until a match is found. /// <para/> /// The search attempts to find the resource starting at the location of the current /// class and attempts every combination of starting namespace and or starting assembly name /// (split on <c>.</c>) concatenated with the <paramref name="name"/>. For example, if the /// type passed is in the namespace <c>Foo.Bar</c> in an assembly named <c>Faz.Baz</c> /// and the name passed is <c>res.txt</c>, the following locations are searched in this order: /// <code> /// 1. res.txt /// 2. Faz.Baz.Foo.Bar.res.txt /// 3. Foo.Bar.res.txt /// 4. Faz.Baz.res.txt /// </code> /// <para/> /// Usage Note: This method effectively treats embedded resources as being in the same /// "class path" as the type that is passed, making it similar to the <c>Class.getResource()</c> /// method in Java. /// </summary> /// <param name="assembly">This assembly.</param> /// <param name="name">The resource name to locate.</param> /// <returns>The resource, if found; if not found, returns <c>null</c>.</returns> /// <exception cref="ArgumentNullException"><paramref name="assembly"/> or <paramref name="name"/> is <c>null</c>.</exception> public static string FindResource(this Assembly assembly, string name) { if (assembly == null) { throw new ArgumentNullException(nameof(assembly)); } if (name == null) { throw new ArgumentNullException(nameof(name)); } var key = new TypeAndResource(null, name); return(resourceCache.GetOrAdd(key, (key) => { string[] resourceNames = assembly.GetManifestResourceNames(); string resourceName = resourceNames.Where(x => x.Equals(name)).FirstOrDefault(); // If resourceName is not null, we have an exact match, don't search if (resourceName == null) { string assemblyName = assembly.GetName().Name; int lastDot = assemblyName.LastIndexOf('.'); do { // Search by assembly name only string resourceToFind = string.Concat(assemblyName, ".", name); TryFindResource(resourceNames, null, resourceToFind, name, out resourceName); // Continue searching by removing sections after the . from the assembly name // until we have a match. lastDot = assemblyName.LastIndexOf('.'); assemblyName = lastDot >= 0 ? assemblyName.Substring(0, lastDot) : null; } while (assemblyName != null && resourceName == null); if (resourceName == null) { // Try again without using the assembly name TryFindResource(resourceNames, null, name, name, out resourceName); } } return resourceName; })); }
/// <summary> /// Aggressively searches to find a resource based on a <see cref="Type"/> and resource name. /// </summary> /// <param name="assembly">this assembly</param> /// <param name="type">a type in the same namespace as the resource</param> /// <param name="name">the resource name to locate</param> /// <returns>the resource, if found; if not found, returns <c>null</c></returns> public static string FindResource(this Assembly assembly, Type type, string name) { name = ConvertResourceName(name); string resourceName; TypeAndResource key = new TypeAndResource(type, name); if (!resourceCache.TryGetValue(key, out resourceName)) { string[] resourceNames = assembly.GetManifestResourceNames(); resourceName = resourceNames.Where(x => x.Equals(name)).FirstOrDefault(); // If resourceName is not null, we have an exact match, don't search if (resourceName == null) { string assemblyName = type.GetTypeInfo().Assembly.GetName().Name; string namespaceName = type.GetTypeInfo().Namespace; // Search by assembly + namespace string resourceToFind = string.Concat(namespaceName, ".", name); if (!TryFindResource(resourceNames, assemblyName, resourceToFind, name, out resourceName)) { string found1 = resourceName; // Search by namespace only if (!TryFindResource(resourceNames, null, resourceToFind, name, out resourceName)) { string found2 = resourceName; // Search by assembly name only resourceToFind = string.Concat(assemblyName, ".", name); if (!TryFindResource(resourceNames, null, resourceToFind, name, out resourceName)) { // Take the first match of multiple, if there are any resourceName = found1 ?? found2 ?? resourceName; } } } } resourceCache[key] = resourceName; } return(resourceName); }