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

Вот что мне нужно сделать:

object foo = GetFoo();
Type t = typeof(BarType);
(foo as t).FunctionThatExistsInBarType();

Можно ли что-то подобное сделать?


person Marek Grzenkowicz    schedule 21.01.2009    source источник
comment
Откуда вы знаете, что это строка, которую вы хотите поставить слева от равенства в последней строке вашего примера? Другими словами, что вы собираетесь делать с bar, что означает, что вы не можете использовать object bar = (object) foo; ?   -  person Martin Brown    schedule 21.01.2009
comment
Я не знаю, строка была только примером. Я отредактировал фрагмент.   -  person Marek Grzenkowicz    schedule 21.01.2009


Ответы (5)


Нет, ты не можешь. C # не поддерживает утиный ввод.

Вы должны реализовать интерфейс и привести к нему.

(Однако попытки сделать это есть. Посмотрите Duck Typing Project для примера.)

person Quassnoi    schedule 21.01.2009
comment
Да, ты можешь. Посмотрите ответ GvS :-) - person Rune Grimstad; 21.01.2009
comment
Вам по-прежнему необходимо реализовать интерфейс (в данном случае IConvertible) и заранее знать тип, к которому выполняется преобразование. - person Quassnoi; 21.01.2009
comment
Я пометил этот ответ как принятый, так как я использовал совет Квассного и реализовал интерфейс для его преобразования, но ответы GvS и shuggycouk тоже великолепны. - person Marek Grzenkowicz; 21.01.2009

Вы можете использовать метод Convert.ChangeType.

object foo = GetFoo(); 
Type t = typeof(string);
string bar = (string)Convert.ChangeType(foo, t);
person GvS    schedule 21.01.2009
comment
Это полезно, только если объект реализует IConvertible. - person Quassnoi; 21.01.2009
comment
Я использовал строку только в качестве примера. Проблема в том, что я не знаю целевой тип, поэтому я не могу выполнить этот xyz bar = (xyz) Convert.Change ... cast. - person Marek Grzenkowicz; 21.01.2009
comment
Convert.ChangeType - это не преобразование, а преобразование. т.е. создание нового объекта указанного типа. Если вы хотите использовать ICollection, это не сработает. - person Hoots; 29.07.2014
comment
? в вашем примере здесь, я думаю, var bar = (string) foo; будет делать то же самое ... вы не использовали Convert.ChangeType, но вы использовали тот момент, когда использовали (string)... - person derHugo; 12.07.2018

Поскольку в C # была добавлена ​​динамика, я думаю, что мы можем сделать это следующим образом:

class Program {
    static void Main(string[] args) {
        List<int> c = new List<int>(); 
        double i = 10.0;
        Type intType = typeof(int);
        c.Add(CastHelper.Cast(i, intType)); // works, no exception!
    }
}

class CastHelper {
    public static dynamic Cast(object src, Type t) {
        var castMethod = typeof(CastHelper).GetMethod("CastGeneric").MakeGenericMethod(t);
        return castMethod.Invoke(null, new[] { src });
    }
    public static T CastGeneric<T>(object src) {
        return (T)Convert.ChangeType(src, typeof(T));
    }
}
person rmrfhome    schedule 11.02.2016
comment
Это сработало как шарм, в отличие от других решений. - person BrotherOdin; 01.11.2017

В вашем исходном вопросе был недостаток в том, что вы просили рассматривать переменную как тип, который неизвестен во время компиляции, но обратите внимание, что у вас есть строка, определенная с левой стороны, когда вы объявляете свою переменную. C #, начиная с версии 3.5, имеет статическую типизацию.

Как только динамический станет доступен, вы можете сделать что-то вроде этого:

dynamic foo = GetFoo();
foo.FunctionThatExistsInBarType(); 

Когда вы не знаете, что это за тип, но знаете, что он всегда будет поддерживать метод экземпляра FunctionThatExistsInBarType ();

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

// any of these can be determined at runtime
Type t = typeof(Bar);
string methodToCall = "FunctionThatExistsInBarType";
Type[] argumentTypes = new Type[0];
object[] arguments = new object[0];
object foo;
// invoke the method - 
// example ignores overloading and exception handling for brevity
// assumption: return type is void or you don't care about it
t.GetMethod(methodToCall, BindingFalgs.Public | BindingFlags.Instance)
    .Invoke(foo, arguments);
person ShuggyCoUk    schedule 21.01.2009
comment
Тип строки был только примером (не очень хорошим); Я отредактировал фрагмент кода, надеюсь, теперь он понятнее. - person Marek Grzenkowicz; 21.01.2009
comment
Правильно - для этого (до динамического) вы должны использовать отражение. Я отредактирую, чтобы учесть это - person ShuggyCoUk; 21.01.2009

Если вы знаете все необходимые типы во время компиляции, утиный ввод возможен (вроде):

class BarFoo {}
class Foo {}
class Bar {}

class Program
{
    static void Main( )
    {
        var foo = new Foo( );
        var bar = new Bar( );
        var barfoo = new BarFoo( );

        Console.WriteLine(DoStuff(foo));
        Console.WriteLine(DoStuff(bar));
        Console.WriteLine(DoStuff(barfoo));

    }

    static string DoStuff(Foo foo) { return "DoStuff(Foo foo)"; }
    static string DoStuff(Bar bar) { return "DoStuff(Bar bar)"; }
    static string DoStuff(Base fb) { return "DoStuff(object fb)"; }
}

Вывод:

Dostuff(Foo foo)
Dostuff(Bar bar);
DoStuff(object fb);

Если вы в конечном итоге реализуете множество методов, которые в основном делают то же самое, подумайте о реализации интерфейса.

person Marcus Hansson    schedule 23.07.2013
comment
Эээ .. Четыре года спустя .. Ну ладно. - person Marcus Hansson; 23.07.2013
comment
Это не утиная печать. Это полиморфизм (применительно к функции DoStuff). - person Tomas Aschan; 24.10.2014