0

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

Class Prime
{
Type t;
obj[] param;
public void addobj(object obj)
{
     t = obj.GetType();
    param = obj.GetParameter();
}

public object createobj() { return Activators.CreateInstanse(t,param)

} }

Чтобы вызов adobj выглядел следующим образом:

addobj(new Person("Vasya", 17);
A B
  • 408
  • 1
    Вот здесь я игрался с этой темой. – aepot Nov 08 '20 at 00:40
  • 2
    невозможно узнать с помощью какого именно конструктора был создан объект, можно лишь получить список всех конструкторов указанного типа, из конструкторов, соответственно типы параметров – Grundy Nov 08 '20 at 01:07
  • 1
    а что вы пытаетесь решить таким кодом? т.к. сейчас это выглядит очень странно, вы передаете уже созданный объект, чтобы потом создать его через Activator? – Exploding Kitten Nov 08 '20 at 09:32
  • @ExplodingKitten, не его, а новый объект, с такими же параметрами – Grundy Nov 08 '20 at 09:37
  • @ExplodingKitten создать список историии объектов, чтобы хранились не их экемпляры, а их "шаблоны". – A B Nov 08 '20 at 09:47
  • 1
    Можете более конкретный пример привести? Пока выглядит так, что решения нет. Быть может на более высоком уровне вы выбрали неверный подход к решению. Расскажите задачу с примером. Напишитп аоспроизводимый пример, например на базе консольного приложения, покажите его нам. – aepot Nov 08 '20 at 09:58
  • а вам нужно хранить историю для любого типа объекта? – Exploding Kitten Nov 08 '20 at 10:27
  • если для конкретных типов, выглядит как паттерн Memento, вы сохраняете внутреннее состояние в некую структуру, а в будущем, можете создать новый объект из этой структуры, и это все можно сделать не раскрывая реализацию объекта – Exploding Kitten Nov 08 '20 at 10:34
  • @aepot , у меня был предыдущий вопрос на эту тему. Там я смогла реализовать то, что хотела. Единственным минусом, как по мне, являлось то, что необходимо было выводить новый тип данных для аргументов, и хранить обработку в конструкторах классов. – A B Nov 08 '20 at 17:24

1 Answers1

2

Аргументы метода передаются через стек, т.е. по окончанию вызова конструктора о параметрах, переданных в него, ничего не известно. Чтобы узнать, какие аргументы были переданы в конструктор есть несколько способов.

Кэширование

Зачастую аргументы, передаваемые в конструктор, сохраняются в полях новосозданного объекта. С помощью рефлексии мы можем изъять эти поля. Лично я данный метод не рекомендую, т.к. вы должны знать внутреннее устройство типа, а т.к. эти поля, обычно, приватные, то метаданные о них от версии к версии могут меняться.

public sealed class Some
{
    readonly int x;
public Some(int x)
{
    this.x = x;
}

}

static void Main()
{
    var some = new Some(5);

    // проверки опущены
    Console.WriteLine(some.GetField("x").GetValue(some)); // 5
}

Сохранить аргументы локально

Можно сохранить аргументы локально в стеке метода, вызывающего конструктор. Это самый простой и безопасный способ.

public sealed class Node
{
    readonly Node? next;
public Node(Node? next = null)
{
    this.next = next;
}

}

static void Main()
{
    var next = new Node();
    var node = new Node(next);

    // `next` - это и есть аргумент 2-го конструктора
}

Прокси-класс

Если уж очень хочется сохранить аргументы глобально, при этом не вскрывая класс изнутри, то можно использовать прокси-класс.

public sealed class Some
{
    readonly int a, b, c;
public Some(int a, int b, int c)
{
    (this.a, this.b, this.c) = (a, b, c);
}

}

public sealed class SomeProxy
{
    public Some Instance { get; }
    public int PassedA { get; }
    public int PassedB { get; }
    public int PassedC { get; }

    public SomeProxy(int a, int b, int c)
    {
        Instance = new Some(a, b, c);
        (PassedA, PassedB, PassedC) = (a, b, c);
    }
}
public interface ISomeOwner : IDisposable
{
    Some Instance { get; }

    void Remove();
}
public class SomeStorage
{
    readonly LinkedList<SomeProxy> proxies;

    ⋮

    public ISomeOwner Add(int a, int b, int c)
    {
        var proxy = new SomeProxy(a, b, c);
        var node = proxies.Add(proxy);

        ⋮

        return new SomeOwner(proxy.Instance, node);
    }

    ⋮

    seales class SomeOwner : ISomeOwner
    {
        internal SomeOwner(Some some, LinkedListNode<Some> node)
        {
            ⋮
        }

        ⋮
    }
}

Важно! Это псевдо-код. Вам не следует его копировать в проекты для релиза.

После создания и добавления объекта типа Some в наше заранее созданное хранилище мы можем обращаться к аргументам, переданным в его конструктор, с помощью получения их из соответствующего SomeProxy.


В итоге вполне простыми способами мы можем реализовать хранение аргументов конструктора в самом классе, во внешних локальном и глобальном хранилищах. Способ выбирайте сами в зависимости от вашей ситуации.


Получить конструктор от объекта заренее известного типа довольно просто, но если вы хотите получить его от generic'а, то тут придётся постараться. Это я про 3-й способ — SomeProxy. Его можно заменить на вот такой класс:

public sealed class Proxy<out T>
{
    public T Instance { get; }
    public ConstructorInfo UsedConstructor { get; }
    public IReadOnlyList<object?> PassedArgs { get; }
public Proxy(params object?[]? args)
{
    if (!FindConstructor(out var ctor, args)) throw new ArgumentException(&quot;Suitable constructor not found&quot;, nameof(args));

    UsedConstructor = ctor;
    PassedArgs = new ReadOnlyCollection&lt;object?&gt;(args ?? Array.Empty&lt;object?&gt;);
    Instance = UsedConstructor.Invoke(PassedArgs);
}

static bool FindConstructorByArgs([MaybeNullWhen(false)] out ConstructorInfo constructor, params object?[]? args)
{
    constructor = null!;

    var ctorArgTypes = args is null ? Array.Empty&lt;Type?&gt;() :
        args.Select(a =&gt; a is null ? null : a.GetType());
    var ctors = typeof(T).GetConstructors();
    ConstructorInfo? ctor = null;

    foreach (var _ctor in ctors)
    {
        var args = _ctor.GetParameters();

        if (args.Length != ctorArgTypes.Length) continue;

        for (var i = 0; i &lt; args.Length; i++)
        {
            if (ctorArgTypes[i] is null &amp;&amp;
                args[i].ParameterType != typeof(Nullable&lt;&gt;) &amp;&amp;
                args[i].ParameterType.IsValueType)
                continue;
            if (args[i].ParameterType != ctorArgTypes[i])
                continue;
        }

        ctor = _ctor;

        break;
    }

    if (ctor is null) return false;

    constructor = ctor;

    return true;
}

}

return
  • 2,740