maui-linux/Xamarin.Forms.Core/Registrar.cs

167 строки
4.5 KiB
C#
Исходник Обычный вид История

2016-03-22 23:02:25 +03:00
using System;
using System.Collections.Generic;
using System.ComponentModel;
2016-03-22 23:02:25 +03:00
using System.Linq;
using System.Reflection;
namespace Xamarin.Forms
{
// Previewer uses reflection to bind to this method; Removal or modification of visibility will break previewer.
internal static class Registrar
{
internal static void RegisterAll(Type[] attrTypes) => Internals.Registrar.RegisterAll(attrTypes);
}
}
namespace Xamarin.Forms.Internals
2016-03-22 23:02:25 +03:00
{
[EditorBrowsable(EditorBrowsableState.Never)]
public class Registrar<TRegistrable> where TRegistrable : class
2016-03-22 23:02:25 +03:00
{
readonly Dictionary<Type, Type> _handlers = new Dictionary<Type, Type>();
public void Register(Type tview, Type trender)
{
_handlers[tview] = trender;
}
internal TRegistrable GetHandler(Type type)
{
Type handlerType = GetHandlerType(type);
if (handlerType == null)
return null;
object handler = Activator.CreateInstance(handlerType);
return (TRegistrable)handler;
}
public TOut GetHandler<TOut>(Type type) where TOut : TRegistrable
2016-03-22 23:02:25 +03:00
{
return (TOut)GetHandler(type);
}
public Type GetHandlerType(Type viewType)
2016-03-22 23:02:25 +03:00
{
Type type;
if (LookupHandlerType(viewType, out type))
2016-03-22 23:02:25 +03:00
return type;
// lazy load render-view association with RenderWithAttribute (as opposed to using ExportRenderer)
var attribute = viewType.GetTypeInfo().GetCustomAttribute<RenderWithAttribute>();
if (attribute == null)
{
Register(viewType, null); // Cache this result so we don't have to do GetCustomAttribute again
2016-03-22 23:02:25 +03:00
return null;
}
2016-03-22 23:02:25 +03:00
type = attribute.Type;
if (type.Name.StartsWith("_"))
{
// TODO: Remove attribute2 once renderer names have been unified across all platforms
var attribute2 = type.GetTypeInfo().GetCustomAttribute<RenderWithAttribute>();
if (attribute2 != null)
type = attribute2.Type;
if (type.Name.StartsWith("_"))
{
Register(viewType, null); // Cache this result so we don't work through this chain again
2016-03-22 23:02:25 +03:00
return null;
}
}
Register(viewType, type); // Register this so we don't have to look for the RenderWith Attibute again in the future
return type;
2016-03-22 23:02:25 +03:00
}
bool LookupHandlerType(Type viewType, out Type handlerType)
2016-03-22 23:02:25 +03:00
{
Type type = viewType;
while (type != null)
2016-03-22 23:02:25 +03:00
{
if (_handlers.ContainsKey(type))
{
handlerType = _handlers[type];
return true;
}
2016-03-22 23:02:25 +03:00
type = type.GetTypeInfo().BaseType;
}
handlerType = null;
return false;
2016-03-22 23:02:25 +03:00
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static class Registrar
2016-03-22 23:02:25 +03:00
{
static Registrar()
{
Registered = new Registrar<IRegisterable>();
}
internal static Dictionary<string, Type> Effects { get; } = new Dictionary<string, Type>();
public static IEnumerable<Assembly> ExtraAssemblies { get; set; }
2016-03-22 23:02:25 +03:00
public static Registrar<IRegisterable> Registered { get; }
2016-03-22 23:02:25 +03:00
public static void RegisterAll(Type[] attrTypes)
2016-03-22 23:02:25 +03:00
{
Assembly[] assemblies = Device.GetAssemblies();
if (ExtraAssemblies != null)
{
assemblies = assemblies.Union(ExtraAssemblies).ToArray();
}
Assembly defaultRendererAssembly = Device.PlatformServices.GetType().GetTypeInfo().Assembly;
int indexOfExecuting = Array.IndexOf(assemblies, defaultRendererAssembly);
if (indexOfExecuting > 0)
{
assemblies[indexOfExecuting] = assemblies[0];
assemblies[0] = defaultRendererAssembly;
}
// Don't use LINQ for performance reasons
// Naive implementation can easily take over a second to run
foreach (Assembly assembly in assemblies)
{
foreach (Type attrType in attrTypes)
{
Attribute[] attributes = assembly.GetCustomAttributes(attrType).ToArray();
if (attributes.Length == 0)
continue;
foreach (HandlerAttribute attribute in attributes)
{
if (attribute.ShouldRegister())
Registered.Register(attribute.HandlerType, attribute.TargetType);
}
}
string resolutionName = assembly.FullName;
Attribute[] effectAttributes = assembly.GetCustomAttributes(typeof(ExportEffectAttribute)).ToArray();
if (effectAttributes.Length > 0)
{
var resolutionNameAttribute = (ResolutionGroupNameAttribute)assembly.GetCustomAttribute(typeof(ResolutionGroupNameAttribute));
if (resolutionNameAttribute != null)
{
resolutionName = resolutionNameAttribute.ShortName;
}
2016-03-22 23:02:25 +03:00
foreach (Attribute attribute in effectAttributes)
{
var effect = (ExportEffectAttribute)attribute;
[Core] Allow Registrar.RegisterAll to be run multiple times (#215) There are cases where we run RegisterAll manually to ensure all assemblies have actually been registered. The `Registrar<T>` class does not use the `Dictionary<K, V>.Add` method so it does not throw if there's a clash/duplicate. If we change the `Effect` registration to use the same pattern we can remove errors like this: System.ArgumentException: An item with the same key has already been added. at ThrowArgumentException at offset 0 in file:line:column /Users/builder/data/lanes/2922/977921b7/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/throwhelper.cs:72:0 at Insert at offset 142 in file:line:column /Users/builder/data/lanes/2922/977921b7/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/collections/generic/dictionary.cs:336:0 at Add at offset 0 in file:line:column /Users/builder/data/lanes/2922/977921b7/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/collections/generic/dictionary.cs:192:0 at RegisterAll at <unknown offset> in file:line:column <filename unknown>:0:0 at <unknown method> at <unknown offset> in file:line:column <filename unknown>:0:0 at Invoke at offset 56 in file:line:column /Users/builder/data/lanes/2922/977921b7/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:295:0
2016-06-08 20:30:13 +03:00
Effects [resolutionName + "." + effect.Id] = effect.Type;
2016-03-22 23:02:25 +03:00
}
}
}
DependencyService.Initialize(assemblies);
2016-03-22 23:02:25 +03:00
}
}
}