Một số dạng hạn chế của ý định của bạn là kiến thức của tôi có thể có trong Java và C # thông qua sự kết hợp giữa Chú thích và Mẫu proxy động (tồn tại các triển khai tích hợp cho các proxy động trong Java và C #).
Phiên bản Java
Chú thích:
@Target(ElementType.PARAMETER)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface IntRange {
int min ();
int max ();
}
Lớp Wrapper tạo cá thể Proxy:
public class Wrapper {
public static Object wrap(Object obj) {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new MyInvocationHandler(obj));
}
}
InvocationHandler phục vụ như bỏ qua tại mỗi cuộc gọi phương thức:
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);
}
}
Giao diện ví dụ để sử dụng:
public interface Example {
public void print(@IntRange(min=0,max=100) int num);
}
Phương pháp chính:
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);
Đầu ra:
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
Chú thích (trong thuộc tính được gọi là C #):
[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; }
}
Lớp con 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;
}
}
Ví dụ:
public class ExampleClass
{
public void PrintNum([IntRange(0,100)] int num)
{
Console.WriteLine(num.ToString());
}
}
Sử dụng:
static void Main(string[] args)
{
dynamic myObj = new DynamicProxy(new ExampleClass());
myObj.PrintNum(99);
myObj.PrintNum(-5);
}
Tóm lại, bạn thấy rằng bạn có thể có một cái gì đó tương tự để hoạt động trong Java , nhưng nó không hoàn toàn thuận tiện, bởi vì
- Lớp proxy chỉ có thể được khởi tạo cho các giao diện, tức là lớp của bạn phải thực hiện một giao diện
- Phạm vi cho phép chỉ có thể được khai báo ở cấp độ giao diện
- Việc sử dụng sau này chỉ cần nỗ lực thêm vào lúc đầu (MyInvocationHandler, gói mỗi lần khởi động) cũng làm giảm một chút sự hiểu biết
Các khả năng của lớp DynamicObject trong C # loại bỏ hạn chế giao diện, như bạn thấy trong triển khai C #. Thật không may, hành vi động này loại bỏ sự an toàn của loại tĩnh trong trường hợp này, vì vậy kiểm tra thời gian chạy là cần thiết để xác định xem một cuộc gọi phương thức trên proxy động có được phép hay không.
Nếu những hạn chế đó được chấp nhận cho bạn, thì điều này có thể làm cơ sở cho việc đào sâu hơn!