Как получить значение свойства класса в неисполняемой сборке через отражение

У меня возникают проблемы при получении значения свойства text в неисполняемой сборке; Я читаю сборку с диска через отражение, затем я получаю все классы в сборке для поиска свойства Text в классе формы Windows, который инициализируется конструктором форм win. Пока у меня есть следующий код:

static void Main(string[] args)
{
    Assembly asm = Assembly.LoadFrom(Path.Combine(path, "Assembly.exe"));
    PropertyInfo[] props;
    foreach (Type t in asm.GetTypes())
    {
        var value = t.GetProperty("Text").GetValue(/*Not sure what to put here*/)
    }
}

И вот как дизайнер сгенерировал форму

    Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None
    Me.BackColor = System.Drawing.Color.FromArgb(CType(CType(0, Byte), Integer), CType(CType(128, Byte), Integer), CType(CType(128, Byte), Integer))
    Me.ClientSize = New System.Drawing.Size(234, 181)
    Me.Cursor = System.Windows.Forms.Cursors.Default
    Me.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
    Me.ForeColor = System.Drawing.SystemColors.WindowText
    Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
    Me.Location = New System.Drawing.Point(581, 222)
    Me.MaximizeBox = False
    Me.MinimizeBox = False
    Me.Name = "winform"
    Me.RightToLeft = System.Windows.Forms.RightToLeft.No
    Me.StartPosition = System.Windows.Forms.FormStartPosition.Manual
    Me.Text = "Title"
    Me.fraDías.ResumeLayout(False)
    Me.ResumeLayout(False)

Имейте в виду, что сборка находится на диске и не выполняется, и что я хочу получить значение свойства Text каждой winform (я думаю, что оно должно быть где-то жестко закодировано в сборке, так как оно было сгенерировано конструктором winforms)

Пожалуйста, скажите мне, если это возможно, спасибо!

3 ответа

Решение

Вы не можете прочитать свойство класса, который не был создан! Отсутствующий параметр является экземпляром вашего типа.

Вы должны создать экземпляр типа с object o = Activator.CreateInstance(type); до доступа к его членам (если они не статичны).


Ваша проблема связана с тем, как надстройки (плагины) могут быть загружены во время выполнения. Вот как я сделал Add-In Loader. Ниже я объясню, как вы можете адаптировать его к вашей проблеме. Надстройки должны реализовать IAddIn Интерфейс в моем примере. Вы абсолютно свободны в определении IAddIn, Вы можете определить это так:

public interface IAddIn
{
    bool OnLoad();

    string Version { get; set; }
    string Text { get; set; }
}

Это позволяет вам получить доступ к участникам без отражения.

public class AddInLoader
{
    // Loads all non abstract types implementing IAddIn in all DLLs found in a folder.
    public IList<IAddIn> Load(string folder)
    {
        var addIns = new List<IAddIn>();
        string[] files = Directory.GetFiles(folder, "*.dll");
        foreach (string file in files) {
            addIns.AddRange(LoadFromAssembly(file));
        }
        return addIns;
    }

    // Loads all non abstract types implementing IAddIn found in a file.
    private static IEnumerable<IAddIn> LoadFromAssembly(string fileName)
    {
        Assembly asm = Assembly.LoadFrom(fileName);
        string addInInterfaceName = typeof(IAddIn).FullName;
        foreach (Type type in asm.GetExportedTypes()) {
            Type interfaceType = type.GetInterface(addInInterfaceName);
            if (interfaceType != null &&
               (type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract){
                IAddIn addIn = (IAddIn)Activator.CreateInstance(type);
                addIn.Version = asm.GetName().Version.ToString();
                yield return addIn;
            }
        }
    }
}

Теперь вы можете загрузить и получить доступ к надстройкам следующим образом:

var loader = new AddInLoader();
IList<IAddIn> addIns = loader.Load(folderPath);
foreach (IAddIn addIn in addIns) {
    if (addIn.OnLoad()) {
        Console.WriteLine("Version = {0}, Text = {1}", addIn.Version, addIn.Text);
    }
}

Чтение названий форм во время выполнения:

Вы можете легко адаптировать этот пример. Вместо поиска типов, реализующих интерфейс, ищите типы, происходящие от System.Windows.Forms.Form,

private static IEnumerable<Form> LoadFormsFromAssembly(string fileName)
{
    Assembly asm = Assembly.LoadFrom(fileName);
    foreach (Type type in asm.GetExportedTypes()) {
        if (typeof(Form).IsAssignableFrom(type) &&
           (type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract) {
            Form form = (Form)Activator.CreateInstance(type);
            yield return form;
        }
    }
}

Теперь вы можете получить тексты таких форм:

var forms = LoadFormsFromAssembly(path);
foreach (Form frm in forms) {
    Console.WriteLine(frm.Text);
}

Примечание. Вы должны создавать экземпляры форм, однако вам не нужно открывать (показывать) их. Код работает, только если формы имеют конструктор по умолчанию, т.е. конструктор без параметров.

Ваши требования противоречивы: когда вы загружаете сборку с помощью отражения и создаете экземпляр объекта или пытаетесь получить значение свойства, то происходит то, что какой-то код начинает выполняться, и пути к этому нет.

Помните, что свойства являются просто "синтаксическим сахаром" для пары методов, метода получения и установки. Их текущее значение - не что иное, как значение, возвращаемое методом getter, и когда вы изменяете его значение, вы фактически вызываете его метод setter. Таким образом, чтобы получить значения свойств, вы должны создать некоторый код для запуска, даже если это тривиальный метод get.

Я думаю, возможно, ваше замешательство связано с тем, что вы используете дизайнер для создания формы. В частности, в конструкторе WinForms (например, WPF существенно отличается) все, что он делает, - это автоматически генерирует некоторый код для вас. Установка свойств, размещение и перемещение элементов управления, что происходит под капотом, заключается в том, что он пишет код, который копирует ваши действия во время выполнения, в частности, он кодирует InitializeComponent метод. Значение реального свойства устанавливается, когда вызывается конструктор (который, в свою очередь, вызывает InitializeComponent), и затем вы можете читать / изменять, используя множество свойств.

Чтобы прочитать эти атрибуты конструктора, нужно, чтобы они были жестко закодированы в виде метаданных в некоторой форме, чтобы они просто читались как данные, а не как результат выполнения кода. Это не относится к WinForms, так как он "сохраняет" форму как код.

Вам нужен объект экземпляра для этого типа, чтобы получить значение свойства. Похоже, вы просто хотите проверить, имеет ли тип свойство "Текст" или нет. Вы можете к этому, проверив
bool hasTextProperty = t.GetProperty("Text")!=null;

Другие вопросы по тегам