Eine eingeschränkte Form Ihrer Absicht ist meines Wissens nach in Java und C # durch eine Kombination aus Anmerkungen und dynamischem Proxy-Muster möglich (es gibt integrierte Implementierungen für dynamische Proxys in Java und C #).
Java-Version
Die Anmerkung:
@Target(ElementType.PARAMETER)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface IntRange {
int min ();
int max ();
}
Die Wrapper-Klasse, die die Proxy-Instanz erstellt:
public class Wrapper {
public static Object wrap(Object obj) {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new MyInvocationHandler(obj));
}
}
Der InvocationHandler, der bei jedem Methodenaufruf als Bypass dient:
public class MyInvocationHandler implements InvocationHandler {
private Object impl;
public MyInvocationHandler(Object obj) {
this.impl = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Annotation[][] parAnnotations = method.getParameterAnnotations();
Annotation[] par = null;
for (int i = 0; i<parAnnotations.length; i++) {
par = parAnnotations[i];
if (par.length > 0) {
for (Annotation anno : par) {
if (anno.annotationType() == IntRange.class) {
IntRange range = ((IntRange) anno);
if ((int)args[i] < range.min() || (int)args[i] > range.max()) {
throw new Throwable("int-Parameter "+(i+1)+" in method \""+method.getName()+"\" must be in Range ("+range.min()+","+range.max()+")");
}
}
}
}
}
return method.invoke(impl, args);
}
}
Die Beispiel-Oberfläche zur Verwendung:
public interface Example {
public void print(@IntRange(min=0,max=100) int num);
}
Hauptmethode:
Example e = new Example() {
@Override
public void print(int num) {
System.out.println(num);
}
};
e = (Example)Wrapper.wrap(e);
e.print(-1);
e.print(10);
Ausgabe:
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.print(Unknown Source)
at application.Main.main(Main.java:13)
Caused by: java.lang.Throwable: int-Parameter 1 in method "print" must be in Range (0,100)
at application.MyInvocationHandler.invoke(MyInvocationHandler.java:27)
... 2 more
C # -Version
Die Anmerkung (in C # Attribut genannt):
[AttributeUsage(AttributeTargets.Parameter)]
public class IntRange : Attribute
{
public IntRange(int min, int max)
{
Min = min;
Max = max;
}
public virtual int Min { get; private set; }
public virtual int Max { get; private set; }
}
Die DynamicObject-Unterklasse:
public class DynamicProxy : DynamicObject
{
readonly object _target;
public DynamicProxy(object target)
{
_target = target;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
TypeInfo clazz = (TypeInfo) _target.GetType();
MethodInfo method = clazz.GetDeclaredMethod(binder.Name);
ParameterInfo[] paramInfo = method.GetParameters();
for (int i = 0; i < paramInfo.Count(); i++)
{
IEnumerable<Attribute> attributes = paramInfo[i].GetCustomAttributes();
foreach (Attribute attr in attributes)
{
if (attr is IntRange)
{
IntRange range = attr as IntRange;
if ((int) args[i] < range.Min || (int) args[i] > range.Max)
throw new AccessViolationException("int-Parameter " + (i+1) + " in method \"" + method.Name + "\" must be in Range (" + range.Min + "," + range.Max + ")");
}
}
}
result = _target.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, _target, args);
return true;
}
}
Die ExampleClass:
public class ExampleClass
{
public void PrintNum([IntRange(0,100)] int num)
{
Console.WriteLine(num.ToString());
}
}
Verwendung:
static void Main(string[] args)
{
dynamic myObj = new DynamicProxy(new ExampleClass());
myObj.PrintNum(99);
myObj.PrintNum(-5);
}
Abschließend sehen Sie, dass Sie so etwas in Java zum Laufen bringen können , aber es ist nicht ganz praktisch, weil
- Proxy-Klasse kann nur für Schnittstellen instanziiert werden, dh Ihre Klasse muss eine Schnittstelle implementieren
- Zulässiger Bereich kann nur auf Schnittstellenebene deklariert werden
- Die spätere Verwendung ist anfangs nur mit zusätzlichem Aufwand verbunden (MyInvocationHandler, bei jeder Instanziierung umbrechen), was auch die Verständlichkeit leicht beeinträchtigt
Die Funktionen der DynamicObject-Klasse in C # heben die Schnittstelleneinschränkung auf, wie Sie in der C # -Implementierung sehen. Leider wird durch dieses dynamische Verhalten die Sicherheit statischer Typen in diesem Fall aufgehoben. Daher sind Laufzeitprüfungen erforderlich, um festzustellen, ob ein Methodenaufruf für den dynamischen Proxy zulässig ist.
Wenn diese Einschränkungen für Sie akzeptabel sind, kann dies als Grundlage für das weitere Graben dienen!