Параметр универсального типа и динамический тип

#c# #generics #c#-4.0 #dynamic

#c# #универсальные #c #-4.0 #динамический

Вопрос:

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

 class Program
{
    static void Main(string[] args)
    {
        HasGenericMethod hgm = new HasGenericMethod();
        Console.WriteLine(hgm.GenericMehtod("string"));
        Console.WriteLine(hgm.GenericMehtod((object)"object"));

        Console.WriteLine();

        Console.WriteLine(hgm.TrickyGenericMethod("string"));
        Console.WriteLine(hgm.TrickyGenericMethod((object)"object"));


        Console.ReadKey();
    }
}

class HasGenericMethod
{
    public string GenericMehtod<T>(T param)
    {
        return  "Type of T:t"   typeof(T)   "tType of param:t"   param.GetType();
    }

    public string TrickyGenericMethod<T>(T param)
    {
        if (typeof(T) != param.GetType())
        {
            return TrickyGenericMethod((dynamic)param);
        }
        return "Type of T:t"   typeof(T)   "tType of param:t"   param.GetType(); ;
    }
}
  

Ответ №1:

является ли оператор if в TrickyGenericMethod плохой идеей

Да, мне кажется, что это так. В частности, если вы вызываете это из другой сборки, используя значение внутреннего типа (и оно проходит по этому пути один раз), «компилятор времени выполнения» будет использовать наилучший доступный тип для dynamic вызова… таким образом, вы получите переполнение стека.

РЕДАКТИРОВАТЬ: Пример кода для этого…

 // Library.cs
using System;

public class Library
{
    public static void Foo<T>(T value)
    {
        Console.WriteLine("{0} : {1}", typeof(T), value.GetType());
        if (typeof(T) != value.GetType())
        {
            dynamic d = value;
            Foo(d);
        }
    }
}

// Test.cs
class Internal {}
class Program
{
    static void Main(string[] args)  
    {
        Library.Foo<object>(new Internal());
    }
}
  

Результат:

 System.Object : Internal
System.Object : Internal
...
System.Object : Internal

Process is terminated due to StackOverflowException.
  

Могут быть и другие подобные ситуации, когда это тоже не совсем работает. Я бы постарался, насколько это возможно, не полагаться на typeof(T) == value.GetType() .

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

Комментарии:

1. Спасибо, Джон. Я безуспешно пытался понять, как DRT решает, какой тип использовать в этом случае. Вы знаете, где это было бы задокументировано?

2. @SeattleLeonard: Боюсь, что спецификация C # оставляет это в основном определяемым реализацией. Я поищу немного больше документации позже.

3. @jbtule: Не так ли? Даже из другой сборки? Хм … надо попробовать это завтра.

4. @jbtule: Мне только что удалось спровоцировать переполнение стека… опубликую свой код.

5. @jbtule упс, моя ошибка, я использовал библиотеку. Foo(new Internal()); для начала.

Ответ №2:

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

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

Комментарии:

1. Вы правы, что я должен просто использовать GetType и покончить с этим. На самом деле это то, с чем я решил пойти, но вы ошибаетесь в паре вещей. Передаваемый параметр в данном случае не является динамическим. Если ссылка на параметр в вызывающей функции была на интерфейс, то это не будет == на GetType(). Во-вторых, я соглашусь с Джоном, что даже когда передается динамический параметр, он все равно может быть неравным.