Beberapa bentuk terbatas dari niat Anda adalah setahu saya mungkin di Jawa dan C # melalui kombinasi Anotasi dan Pola Proksi Dinamis (terdapat implementasi bawaan untuk proxy dinamis di Jawa dan C #).
Versi Java
Anotasi:
@Target(ElementType.PARAMETER)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface IntRange {
int min ();
int max ();
}
Kelas Wrapper membuat instance Proxy:
public class Wrapper {
public static Object wrap(Object obj) {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new MyInvocationHandler(obj));
}
}
InvocationHandler berfungsi sebagai bypass pada setiap panggilan metode:
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);
}
}
Contoh-Antarmuka untuk Penggunaan:
public interface Example {
public void print(@IntRange(min=0,max=100) int num);
}
Metode Utama:
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);
Keluaran:
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 # -Versi
Anotasi (dalam C # disebut atribut):
[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; }
}
Sub-Kelas DynamicObject:
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;
}
}
ExampleClass:
public class ExampleClass
{
public void PrintNum([IntRange(0,100)] int num)
{
Console.WriteLine(num.ToString());
}
}
Pemakaian:
static void Main(string[] args)
{
dynamic myObj = new DynamicProxy(new ExampleClass());
myObj.PrintNum(99);
myObj.PrintNum(-5);
}
Kesimpulannya, Anda melihat bahwa Anda bisa mendapatkan sesuatu seperti itu untuk bekerja di Jawa , tetapi itu tidak sepenuhnya nyaman, karena
- Kelas proxy hanya dapat digunakan untuk antarmuka, yaitu kelas Anda harus mengimplementasikan antarmuka
- Range yang Diizinkan hanya dapat dideklarasikan pada level antarmuka
- Penggunaan kemudian datang hanya dengan upaya ekstra di awal (MyInvocationHandler, membungkus di setiap instansi) yang juga sedikit mengurangi pemahaman
Kemampuan kelas DynamicObject di C # menghapus batasan antarmuka, seperti yang Anda lihat dalam implementasi C #. Sayangnya, perilaku dinamis ini menghapus keamanan tipe statis dalam kasus ini, jadi pemeriksaan runtime diperlukan untuk menentukan apakah metode panggilan pada proxy dinamis diizinkan.
Jika batasan tersebut dapat diterima untuk Anda, maka ini dapat berfungsi sebagai dasar untuk penggalian lebih lanjut!