Приведение коробочного объекта обратно к исходному типу


Я ожидаю, что есть один из двух ответов на этот вопрос, либо невозможный, либо чрезвычайно простой, и я пропустил очевидный запрос Google.

Основная проблема заключается в том, что у меня есть универсальный объект, передаваемый через EventHandler, который блокирует объект и запутывает истинный тип; только во время выполнения я знаю, что это за объект. По общему признанию, ключевое слово dynamic может обойти проблему, но я хотел бы не потерять IntelliSense и все остальное, если я могу избежать этого. К тому же, это не решает проблемы нет знание того, каково каждое из свойств родового объекта, не требует большого количества размышлений.

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

Пример:

public class Program
{
    static void Main(string[] args)
    {
        var container = new Container<Containee>(
            new Containee
            {
                Property1 = Guid.NewGuid(),
                Property2 = "I'm a property!",
                Property3 = DateTime.Now
            }
        );

        var boxed = (object)container;

        var originalType = boxed.GetType();

        // DOES NOT COMPILE: would like an operation like this
        // EDIT: Request for more detail
        var actualType = boxed as originalType;
        actualType.Entity.Property2 = "But I like this better.";
    }
}

public class Containee
{
    public Guid Property1 { get; set; } 
    public string Property2 { get; set; }
    public DateTime Property3 { get; set; }
}

public class Container<T>
{
    public Container(T entity)
    {
        Entity = entity;
    }

    public T Entity { get; internal set; }
}

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

Я ожидаю, что есть что-то простое, что я упускаю из виду, или лучший способ обойти это в целом. Суть в том, чтобы иметь возможность завернуть любой предмет в контейнер и выяснить позже, что это было.
5 11

5 ответов:

Идея заключается в том, чтобы определить истинный тип объекта в параметре метода

Это достаточно просто (и вы уже делаете это).
Type actualType = param.GetType();

Это даст вам фактический конкретный тип объекта

И затем приведите этот объект как истинный Тип

Вот тут-то все и выходит из-под контроля. Оператор кастинга в C# (использование которого люди называют "кастингом") может делать две вещи:

  1. использование специфические для типа явные преобразования для создания нового объекта путем применения преобразования к существующему объекту (обратите внимание, что это Новая ссылка, которая создается; тип исходного объекта никогда не изменяется)
  2. разрешить разработчику ссылаться на объект как на тип, находящийся на другом уровне иерархии наследования, чем тот, который предоставлен в данный момент (или интерфейс, реализованный на типе, который находится ниже в иерархии, чем тот, на который имеется ссылка в данный момент)

В в вашем случае первый вариант является правильным; оператор кастинга, как и все операторы, не является полиморфным. То есть оператор применяется только в том случае, если он определен для типа , на который ссылаются, а не для объекта , на который ссылаются. Если вы хотите получить дополнительные разъяснения по этому вопросу, дайте мне знать, но я не думаю, что это имеет отношение к вашему вопросу, поэтому я не буду вдаваться в него дальше, если меня не спросят.

Второй вариант является единственным вариантом, который может реально применяться для вас, но рассмотрим только две причины, по которым вы хотели бы это сделать:

  1. Таким образом, вы можете ссылаться на объект как на конкретный конкретный тип, который находится на более низком уровне, чем в настоящее время (в вашем случае ваш объект является object, так что это почти так же высоко, как он идет)
  2. , чтобы можно было ссылаться на объект как на тип, который выше в иерархии, чтобы можно было обойти скрытые (но не переопределенные) члены.

(подавляющее большинство слепков являются по причине №1)

Причина, по которой вы хотите использовать любой из этих параметров, заключается в том, что вы можете иметь строго типизированный объект и использовать различные члены, определенные для этого типа. Но все эти вещи применимы только к типам, которые Вызнаете , Когда пишете код. Нет смысла приводить к типу, который неизвестен во время компиляции, так как приведение ничего не делает с реальным объектом (он является и должен оставаться его истинным типом; единственное, что изменяется-это тип объекта). переменная , С помощью которой вы ссылаетесь на объект).

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

Прежде всего: это не "бокс". Бокс предназначен для типов значений, таких как structs.

Во-вторых: то, что вам, вероятно, нужно, это либо:

  • отражение во время компиляции, которого в C# нет
  • динамическая генерация кода, которую вы можете сделать (болезненно) с Reflection.Emit.
В-третьих: ваш пример кода делает variable1 as variable2, что на самом деле не имеет смысла. - Что ты собираешься делать после этого? Возможно, есть лучший способ.
var actualType = boxed as originalType;
Просто чтобы мы были на одной странице, позвольте мне объяснить, почему это невозможно.

var это конструкция времени компиляции. Это тождественно прямому объявлению переменной с соответствующим типом. Помимо того, что его легче печатать, он в основном используется для анонимных типов, которые, как подразумевается, не имеют имен.

В любом случае, чтобы добраться до сути вашего вопроса, лучше всего использовать динамическую генерацию кода, либо с Reflection.Emit, либо с CodeDom (последнее гораздо легче понять, если вы этого не делаете знаю ИЛАЗМ, но гораздо медленнее).

В зависимости от того, что вы на самом деле хотите сделать, вам может сойти с рук что-то вроде

if(someObject is Container<Containee>) {
     var container = (Container<Containee>)someObject;
     //...
}
Но, если вы можете ожидать буквально любого типа, хорошо... удачи.

Основная проблема заключается в том, что у меня есть универсальный объект, передаваемый через EventHandler, который помещает объект и запутывает истинный тип; только при я не знаю, что это за объект.

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

В принципе, у вас есть несколько вариантов:

  • Используйте is и делайте разные вещи для разных типов:

    object value = GetValue ();
    if (value is Program)
        ((Program)value).Run ();
    else if (value is Animal)
        ((Animal)value).Run ();
    
  • Если предполагается, что все возможные типы совместно используют набор операций, используйте интерфейс:

    object value = GetValue ();
    IRunnable runnable = (IRunnable)value;
    runnable.Run ();
    
  • Перефразируйте свой вопрос и расширьте свой образец с тем, как вы видите, что он работает после того, как вы сделали "магический кастинг". Это даст нам представление о том, чего вы пытаетесь достичь.

Можно использовать dynamic:

dynamic actualType = boxed;
actualType.Entity.Property2 = "But I like this better.";

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