Простенький этюдик
От: nikov США http://www.linkedin.com/in/nikov
Дата: 26.06.10 06:30
Оценка: 69 (4)
Есть такой тип:

struct Box<T>
{
    private T value;

    public T Value
    {
        get { return value; }
    }

    public Box(T value)
    {
        this.value = value;
    }
}


Надо написать метод, который может принять значение любого типа вида Box<Box<...Box<T>...>> (где T не является инстанциацией типа Box<>), и извлечь значение типа T из самой внутренней "коробки". Желательно, чтобы код получился не громоздким.
Re: Простенький этюдик
От: nikov США http://www.linkedin.com/in/nikov
Дата: 26.06.10 07:15
Оценка:
Здравствуйте, nikov, Вы писали:

N>и извлечь значение типа T из самой внутренней "коробки".


Метод может вернуть это значение, приведенным к object.
Re: Простенький этюдик
От: Mab Россия http://shade.msu.ru/~mab
Дата: 26.06.10 07:36
Оценка:
Здравствуйте, nikov, Вы писали:

Использовать Reflection API можно?
Re[2]: Простенький этюдик
От: nikov США http://www.linkedin.com/in/nikov
Дата: 26.06.10 07:41
Оценка:
Здравствуйте, Mab, Вы писали:

Mab>Использовать Reflection API можно?


Можно обойтись без него (по крайней мере, в явном виде).
Re: Простенький этюдик
От: gecko  
Дата: 26.06.10 09:21
Оценка:
Здравствуйте, nikov, Вы писали:

N>Надо написать метод, который может принять значение любого типа вида Box<Box<...Box<T>...>> (где T не является инстанциацией типа Box<>), и извлечь значение типа T из самой внутренней "коробки". Желательно, чтобы код получился не громоздким.



object Unbox(object box)
{
  if (box.GetType().IsGenericType && box.GetType().GetGenericTypeDefinition() == typeof(Box<>))
  {
    dynamic d = box;
    return Unbox(d.Value);
  }
  else
  {
    return box;
  }
}
Re[2]: Простенький этюдик
От: nikov США http://www.linkedin.com/in/nikov
Дата: 26.06.10 09:24
Оценка:
Здравствуйте, gecko, Вы писали:

G>
G>object Unbox(object box)
G>{
G>  if (box.GetType().IsGenericType && box.GetType().GetGenericTypeDefinition() == typeof(Box<>))
G>  {
G>    dynamic d = box;
G>    return Unbox(d.Value);
G>  }
G>  else
G>  {
G>    return box;
G>  }
G>}
G>


Иногда будет неверно работать.
Re[3]: Простенький этюдик
От: gecko  
Дата: 26.06.10 09:44
Оценка: 16 (1)
Здравствуйте, nikov, Вы писали:

N>Иногда будет неверно работать.


object Unbox(object box)
{
  if (box.GetType().IsGenericType && box.GetType().GetGenericTypeDefinition() == typeof(Box<>))
  {
    Type t = box.GetType().GetGenericArguments()[0];
    dynamic d = box;
    if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Box<>))
    {
      return Unbox(d.Value);
    }
    else
    {
      return d.Value;
    }
  }
  else
  {
    return box;
  }
}
Re[4]: Простенький этюдик
От: nikov США http://www.linkedin.com/in/nikov
Дата: 26.06.10 10:19
Оценка:
Здравствуйте, gecko, Вы писали:

G>
G>object Unbox(object box)
G>{
G>  if (box.GetType().IsGenericType && box.GetType().GetGenericTypeDefinition() == typeof(Box<>))
G>  {
G>    Type t = box.GetType().GetGenericArguments()[0];
G>    dynamic d = box;
G>    if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Box<>))
G>    {
G>      return Unbox(d.Value);
G>    }
G>    else
G>    {
G>      return d.Value;
G>    }
G>  }
G>  else
G>  {
G>    return box;
G>  }
G>}
G>


Как мне подсказал Mab, в некоторых очень особых случаях это тоже будет работать неверно (как, впрочем, и моё решение). И я не представляю, как это исправить, не заменяя полностью dynamic на рефлекшн. Моё решение, как мне кажется, совершенно эквивалентно по поведению этому коду, но не использует никакого рефлекшена (в т.ч. GetType() или typeof), но, правда, использует оператор is, чтобы обойти один очень хитрый баг (на который мне тоже указал Mab).
Re[5]: Простенький этюдик
От: _FRED_ Черногория
Дата: 26.06.10 10:21
Оценка:
Здравствуйте, nikov, Вы писали:

N>Моё решение, …


Не показывай его пожалуйста до вторника хотя бы — раньше до 2010 мне не добраться
Help will always be given at Hogwarts to those who ask for it.
Re: Простенький этюдик
От: desco США http://v2matveev.blogspot.com
Дата: 26.06.10 14:18
Оценка:
Здравствуйте, nikov, Вы писали:

N>Есть такой тип:


N>
N>struct Box<T>
N>{
N>    private T value;

N>    public T Value
N>    {
N>        get { return value; }
N>    }

N>    public Box(T value)
N>    {
N>        this.value = value;
N>    }
N>}
N>


N>Надо написать метод, который может принять значение любого типа вида Box<Box<...Box<T>...>> (где T не является инстанциацией типа Box<>), и извлечь значение типа T из самой внутренней "коробки". Желательно, чтобы код получился не громоздким.


а два метода можно?

    static object Unwrap<T>(Box<T> o)
    {
        dynamic box = o;
        return Unwrap(box.Value);
    }

    static object Unwrap(object v)
    {
        return v;
    }

    static Box<T> New<T>(T value)
    {
        return new Box<T>(value);
    }

    static void Main()
    {
        var box = New(New(New(New(1))));
        var v = Unwrap(box);
    }
Re[2]: Простенький этюдик
От: nikov США http://www.linkedin.com/in/nikov
Дата: 26.06.10 14:20
Оценка:
Здравствуйте, desco, Вы писали:

D>а два метода можно?


Да, можно любой вспомогательный код.

D>
D>    static object Unwrap<T>(Box<T> o)
D>    {
D>        dynamic box = o;
D>        return Unwrap(box.Value);
D>    }

D>    static object Unwrap(object v)
D>    {
D>        return v;
D>    }

D>    static Box<T> New<T>(T value)
D>    {
D>        return new Box<T>(value);
D>    }

D>    static void Main()
D>    {
D>        var box = New(New(New(New(1))));
D>        var v = Unwrap(box);
D>    }
D>


Это будет работать неправильно в некоторых случаях.
Re[3]: Простенький этюдик
От: _FRED_ Черногория
Дата: 26.06.10 18:35
Оценка:
Здравствуйте, nikov, Вы писали:



N>Это будет работать неправильно в некоторых случаях.


Ты потом, если время будет, про эти случаи расскажешь?
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Простенький этюдик
От: gecko  
Дата: 26.06.10 20:11
Оценка: 52 (2) +1
Здравствуйте, _FRED_, Вы писали:

_FR>Ты потом, если время будет, про эти случаи расскажешь?


Тут та же ошибка, что в моём первом варианте:
var a = new Box<object>(1);
var b = new Box<object>(a);
Console.WriteLine(Unwrap(b)); //ожидается Box`1[System.Object], выводит 1

Исправление, видимо, аналогичное:
object Unwrap<T>(Box<Box<T>> o)
{
  dynamic box = o;
  return Unwrap(box.Value);
}

object Unwrap<T>(Box<T> o)
{
  dynamic box = o;
  return box.Value;
}

object Unwrap(object v)
{
  return v;
}
Re[5]: Простенький этюдик
От: nikov США http://www.linkedin.com/in/nikov
Дата: 27.06.10 05:55
Оценка:
Здравствуйте, gecko, Вы писали:

G>Тут та же ошибка, что в моём первом варианте:


Mab показал ещё две ловушки. Первая:

struct Evil : IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression parameter)
    {
        throw new NotImplementedException();
    }
}

Console.WriteLine(Unwrap(New(new Evil()))); // NotImplementedException


А вторая связана с nullable типами, которые автоматически разворачиваются при вызове свойства Value у dynamic.

G>Исправление, видимо, аналогичное:


Да, это исправление очень изящным образом обходит все три. Но ещё одна проблема остается (я не знаю, как решить ее, не отказываясь от dynamic).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.