-
Пишем класс:
public class TestClass
public int Value
}
}
Простенькое тестовое приложение:
static void Main(string[] args)
Замечательно. В списке видим поле FValue. Ещё один тест:
static void Main(string[] args)
);
Console.WriteLine(tc.Value);
}
В консоли видим: 123 456 Приватное поле без проблем поменяли! Не, я понимаю, что такая навороченная система метаинформации -- это мощь и крутота. Ну в чём тогда смысл разграничений private, public? Срам фигой прикрывать?
-
Думаю, пришлось сделать такую "дыру" для возможности сериализации, а особенно десериализации объектов.
-
P.S. к [1]. Я имею в виду автоматические сериализацию и десериализацию с помощью атрибута SerializableAttribute.
-
Сильно подозреваю, что и сей класс, и сей тест-юнит - в одной сборке, посему хак и удался. Подозреваю, что ежели разнести их в разные сборки - нихрена не выйдет.
-
>>Ломброзо © (13.08.05 00:51) [3]
>Сильно подозреваю, что и сей класс, и сей тест-юнит - в одной сборке, посему хак и удался. Подозреваю, что ежели разнести их в разные сборки - нихрена не выйдет.
Подозрения неверны. Выйдет.
-
Вот один пример. Решил я вчера попробовать создать потомка TreeView. Ну думаю, щас оконную процедуру поменяю и все хорошо. А где брать WM_, TVM_ etc.? Директива uses Windows автоматически включает сборки Borland в мою сборку, чего не очень-то хотелось. После копания по иерархии FCL оказалось, что все эти константы имеются в составе System.Windows.Forms в защищенном (или даже приватном) классе NativeMethods. В защищенном - это значит, что разработчики, стремясь к платформо-независимости, не желают открывать публичный доступ для внутренней реализации их классов. Но сделать это можно. Я поступил так:
interface
uses
System.Reflection;
type
PropertyAdapter = class
protected
procedure CloneProperties(SourceAssemblyName, SourceTypeName: System.string); virtual;
constructor Create(SourceAssemblyName, SourceTypeName: System.string); virtual;
end;
NativeMethodsAdapter = class(PropertyAdapter)
private
protected
public
fWM_CREATE: Integer;
fWM_MOVE, fWM_MOVING: Integer;
constructor Create; reintroduce; virtual;
property WM_MOVE: Integer read fWM_MOVE;
property WM_MOVING: Integer read fWM_MOVING;
property WM_CREATE: Integer read fWM_CREATE;
end;
function GetCurrentAppDomainAssembly(AssemblyName: System.string): Assembly;
function GetCurrentAppDomainAssemblyObject(SourceAssemblyName, SourceTypeName: System.string): System.object;
implementation
function GetCurrentAppDomainAssembly(AssemblyName: System.string): Assembly;
var
Assemblies : array of Assembly;
I : Integer;
begin
Result := Assembly.LoadWithPartialName(AssemblyName);
if Assigned(Result) then
Exit
else
begin
Assemblies := AppDomain.get_CurrentDomain.GetAssemblies;
for I := Low(Assemblies) to High(Assemblies) do
begin
Result := Assemblies[I];
if Result.GetName.Name.Equals(AssemblyName) then
Exit
else
Result := nil;
end;
end;
end;
function GetCurrentAppDomainAssemblyObject(SourceAssemblyName, SourceTypeName: System.string): System.object;
var
SourceAssembly : Assembly;
SourceType : System.Type;
begin
Result := nil;
SourceAssembly := GetCurrentAppDomainAssembly(SourceAssemblyName);
if Assigned(SourceAssembly) then
with SourceAssembly do
begin
SourceType := GetType(System.string.Format('.', [SourceAssemblyName, SourceTypeName]));
if Assigned(SourceType) then
Result := Activator.CreateInstance(SourceType);
end;
end;
procedure PropertyAdapter.CloneProperties(SourceAssemblyName, SourceTypeName: System.string);
var
MyProps : array of PropertyInfo;
MyProp : PropertyInfo;
MyField : FieldInfo;
SourceObject : System.object;
SourceField : FieldInfo;
begin
SourceObject := GetCurrentAppDomainAssemblyObject(SourceAssemblyName, SourceTypeName);
if Assigned(SourceObject) then
begin
MyProps := GetType.GetProperties;
if Assigned(MyProps) then
for MyProp in MyProps do
begin
SourceField := SourceObject.GetType.GetField(MyProp.Name);
if Assigned(SourceField) then
begin
MyField := GetType.GetField('f' + MyProp.Name);
MyField.SetValue(Self, SourceField.GetValue(SourceObject));
end;
end;
end;
end;
constructor PropertyAdapter.Create(SourceAssemblyName, SourceTypeName: System.string);
begin
inherited Create;
CloneProperties(SourceAssemblyName, SourceTypeName);
end;
constructor NativeMethodsAdapter.Create;
begin
inherited Create('System.Windows.Forms', 'NativeMethods');
end;
end. Как видно из кода, определяя свойства только для чтения с внутренними полями (NativeMethodsAdapter.WM_CREATE read fWM_CREATE) при создании экземпляра можно инициализировать эти поля свойствами защищенного в сборке System.Windows.Forms объекта. Вариант, разумеется не конечный, только тест, но Ваши мнения будут полезны всем нам. Думаю, таким же образом можно создавать и реализацию методов. С другой стороны, взглянув на любую сборку при помощи утилиты Reflector на http://www.aisto.com/roeder/dotnet, понимаешь, что секретов не то что-бы нет, но они как-то так... Короче, не знаю, возмущаться или нет. И с одной стороны, разделяю возмущение автора.
-
Кстати, одно маленькое дополнение. Заметил, что поля fWM_MOVE и т.д. у меня public. Избавляемся от этого:
NativeMethodsAdapter = class(PropertyAdapter) private protected fWM_CREATE: Integer; fWM_MOVE, fWM_MOVING: Integer; public constructor Create; reintroduce; virtual; property WM_MOVE: Integer read fWM_MOVE; property WM_MOVING: Integer read fWM_MOVING; property WM_CREATE: Integer read fWM_CREATE; end;
и далее, по тексту:
procedure PropertyAdapter.CloneProperties(SourceAssemblyName, SourceTypeName: System.string); //..... if Assigned(SourceField) then begin MyField := GetType.GetField('f' + MyProp.Name, BindingFlags.NonPublic or BindingFlags.Instance); MyField.SetValue(Self, SourceField.GetValue(SourceObject)); end; //.....
-
Вот, что нашёл в MSDN (index: "security, reflection"):
Providing access to nonpublic information involves security risks. Code that is allowed to discover nonpublic information on a type can potentially access code, data, and other information you want to keep private. Therefore, .NET Framework security enforces rules that determine to what degree reflection can be used to discover type information and access types. Depending on the operation being performed, a ReflectionPermission or the SecurityPermission for serialization might be required.
...
-
Мда. Вобщем то доступ к закрытым полям и в дельфи(обычной, по крайней мере в семерке) можно получить. procedure TObject.CleanupInstance;
var
ClassPtr: TClass;
InitTable: Pointer;
begin
ClassPtr := ClassType;
InitTable := PPointer(Integer(ClassPtr) + vmtInitTable)^;
while (ClassPtr <> nil) and (InitTable <> nil) do
begin
_FinalizeRecord(Self, InitTable); ClassPtr := ClassPtr.ClassParent;
if ClassPtr <> nil then
InitTable := PPointer(Integer(ClassPtr) + vmtInitTable)^;
end;
end;
-
правда не по имени, но уже кое что :-)
-
а можно fillchar(pointer(obj)^, ... сделать. тоже доступ к полям :)
-
Это распространенное заблуждение - ограничение доступа к полям/методам объекта в ОО не средство обеспечить безопасность, а только реализация инкапсуляции/полиморфизма. К защите данных/методов не имеет отношения. Отмеченые выше типы permission (i.e. ReflectionPermission) не есть способ ограничить доступ к вашему коду, а способ отрезать такой доступ от конкретного чужого кода. Короче, думайте об этом как о простом сокрытии по принципу "меньше знаешь - легче спишь"
-
>ИА (25.10.05 18:34) [11] согласен полностью
|