Привести объект в штучной упаковке обратно в исходный тип - PullRequest
11 голосов
/ 10 июня 2011

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

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

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

РЕДАКТИРОВАТЬ: Идея состоит в том, чтобы иметь возможность определить истинный тип объекта впараметр метода, а затем приведите этот объект к типу true, не зная его заранее.Это всего лишь упрощенный пример.В штучной упаковке, возможно, был неправильный термин.

Пример:

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 ]

8 голосов
/ 10 июня 2011

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

Это достаточно просто (и вы уже делаете это).

Type actualType = param.GetType();

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

и затем приведите этот объект к типу true

Это то, где вещи немного сбиваются с рельсов. Оператор приведения в C # (использование которого люди называют «приведением») может делать две вещи:

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

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

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

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

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

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

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

4 голосов
/ 10 июня 2011

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

Второе: вам, вероятно, понадобится:

  • Отражение во время компиляции, которого нет у C #
  • Динамическая генерация кода, которую вы можете (мучительно) сделать с помощью Reflection.Emit.

В-третьих: ваш пример кода делает variable1 as variable2, что на самом деле не имеет смысла. : \ Что вы собираетесь делать после этого? Возможно, есть лучший способ.

1 голос
/ 10 июня 2011

Вы можете использовать dynamic:

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

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

1 голос
/ 10 июня 2011

Основная проблема заключается в том, что у меня есть универсальный объект передается через 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 ();
    
  • Перефразируйте ваш вопрос и дополните ваш образец тем, как вы видите его работу после того, как вы выполнили «магический кастинг». Это дало бы нам представление о том, чего вы пытаетесь достичь.

1 голос
/ 10 июня 2011
var actualType = boxed as originalType;

Только чтобы мы оказались на одной странице, позвольте мне объяснить, почему это невозможно.

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

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

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

if(someObject is Container<Containee>) {
     var container = (Container<Containee>)someObject;
     //...
}

Но, если вы можете ожидать буквально любой тип, хорошо ... удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...