Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
internal abstract class MultimethodFactory
{
private enum ParameterKind
{
In,
Out,
Ref
}
private sealed class Dispatcher
{
private readonly Delegate _function;
private readonly Type[] _parameterTypes;
internal Delegate Function
{
get
{
return _function;
}
}
internal Dispatcher(Delegate function, Type[] parameterTypes)
{
Debug.Assert(null != function);
Debug.Assert(null != parameterTypes && parameterTypes.Length > 0);
_function = function;
_parameterTypes = parameterTypes;
}
internal Type GetParameterType(int index)
{
return _parameterTypes[index];
}
// IsCompatibleArgTypeArray
/// <summary>
/// Check compatibility of parameter types versus types of real arguments. Compatibility
/// is based on contravariance of argument types.
/// </summary>
/// <param name="argTypes"></param>
/// <returns></returns>
internal bool IsCompatibleArgTypeArray(Type[] argTypes)
{
Debug.Assert(null != argTypes && argTypes.Length > 0);
Debug.Assert(_parameterTypes.Length == argTypes.Length);
for (int i = 0; i < _parameterTypes.Length; ++i)
{
if (!_parameterTypes[i].IsAssignableFrom(argTypes[i]))
return false;
}
return true;
}
internal bool CompareParameterTypeArray(Type[] parameterTypes)
{
Debug.Assert(null != parameterTypes && parameterTypes.Length > 0);
Debug.Assert(parameterTypes.Length == _parameterTypes.Length);
return CompareTypeArrays(_parameterTypes, parameterTypes);
}
private static bool CompareTypeArrays(Type[] types1, Type[] types2)
{
Debug.Assert(null != types1 && types1.Length > 0);
Debug.Assert(null != types2 && types2.Length > 0);
Debug.Assert(types1.Length == types2.Length);
for (int i = 0; i < types1.Length; ++i)
{
if (types1[i] != types2[i])
return false;
}
return true;
}
}
private readonly int _parameterCount;
private readonly List<Dispatcher> _dispatchers = new List<Dispatcher>();
internal protected MultimethodFactory(int parameterCount)
{
Debug.Assert(parameterCount > 0);
_parameterCount = parameterCount;
}
internal protected void CreateDispatcher(Delegate function)
{
Debug.Assert(null != function);
ParameterInfo[] parameters = function.Method.GetParameters();
if (parameters.Length != _parameterCount)
throw new Exception();
Type[] parameterTypes = new Type[parameters.Length];
for (int i = 0; i < parameterTypes.Length; ++i)
{
Type baseParameterType;
if (ParameterKind.In != GetParameterKind(parameters[i], out baseParameterType))
throw new ArgumentException("\"out\" and \"ref\" parameters not supported");
parameterTypes[i] = baseParameterType;
}
foreach (Dispatcher dispatcher in _dispatchers)
{
if (dispatcher.CompareParameterTypeArray(parameterTypes))
throw new ArgumentException("Parameter types exactly match existing method");
}
_dispatchers.Add(new Dispatcher(function, parameterTypes));
}
internal protected object InternalInvoke(params object[] args)
{
Debug.Assert(null != args);
if (_parameterCount != args.Length)
throw new Exception();
Type[] argTypes = new Type[args.Length];
for (int i = 0; i < argTypes.Length; ++i)
argTypes[i] = args[i].GetType();
List<Dispatcher> compatibleDispatchers = new List<Dispatcher>();
foreach (Dispatcher @delegate in _dispatchers)
{
if (@delegate.IsCompatibleArgTypeArray(argTypes))
compatibleDispatchers.Add(@delegate);
}
if (0 == compatibleDispatchers.Count)
throw new Exception("No compatible method implementation");
Dispatcher dispatcher = GetMostCompatibleDispatcher(compatibleDispatchers.ToArray(), argTypes);
if (null != dispatcher)
return dispatcher.Function.DynamicInvoke(args);
throw new Exception("Ambiguous method implementations");
}
private static ParameterKind GetParameterKind(ParameterInfo parameter, out Type baseParameterType)
{
Debug.Assert(null != parameter);
baseParameterType = null;
if (!parameter.ParameterType.FullName.EndsWith("&", StringComparison.Ordinal))
{
baseParameterType = parameter.ParameterType;
return ParameterKind.In;
}
string parameterTypeName = parameter.ParameterType.FullName;
baseParameterType = Type.GetType(parameterTypeName.Substring(0, parameterTypeName.Length - 1));
return parameter.IsOut ? ParameterKind.Out : ParameterKind.Ref;
}
// GetTypeGenerality
/// <summary>
/// Get generality (inverse of specificity) of derived type compared to base type.
/// </summary>
/// <remarks>
/// 0 means that derived type is exactly the same as base type.
/// </remarks>
/// <param name="baseType"></param>
/// <param name="derivedType"></param>
/// <returns></returns>
private static int GetTypeGenerality(Type baseType, Type derivedType)
{
Debug.Assert(null != baseType);
Debug.Assert(null != derivedType);
Debug.Assert(baseType.IsAssignableFrom(derivedType));
int generality = 0;
Type temp = derivedType;
while (derivedType != baseType)
{
derivedType = derivedType.BaseType;
++generality;
}
return generality;
}
private Dispatcher GetMostCompatibleDispatcher(Dispatcher[] compatibleDispatchers, Type[] argTypes)
{
Debug.Assert(null != compatibleDispatchers && compatibleDispatchers.Length > 0);
Debug.Assert(null != argTypes && argTypes.Length > 0);
if (1 == compatibleDispatchers.Length)
return compatibleDispatchers[0];
int[] generalities = new int[compatibleDispatchers.Length];
for (int i = 0; i < generalities.Length; ++i)
generalities[i] = int.MaxValue;
for (int i = 0; i < _parameterCount; ++i)
{
int minimumGenerality = int.MaxValue;
for (int j = 0; j < generalities.Length; ++j)
{
// Only check generality for method if it hasn't already been eliminated.
if (-1 != generalities[j])
{
generalities[j] = GetTypeGenerality(compatibleDispatchers[j].GetParameterType(i), argTypes[i]);
if (generalities[j] < minimumGenerality)
minimumGenerality = generalities[j];
}
}
int dispatcherCount = 0;
int lastDispatcherIndex = -1;
for (int j = 0; j < generalities.Length; ++j)
{
if (generalities[j] > minimumGenerality)
{
generalities[j] = -1;
}
else if (-1 != generalities[j])
{
lastDispatcherIndex = j;
++dispatcherCount;
}
}
Debug.Assert(lastDispatcherIndex >= 0);
if (1 == dispatcherCount)
// We've found the single most compatible dispatcher.
return compatibleDispatchers[lastDispatcherIndex];
}
return null;
}
}
Comments
- Anonymous
November 10, 2008
I read a couple of interesting articles on the subject of multiple dispatch last night. The first, entitled