В С#, когда Type.FullName возвращает null?

MSDN для Type.FullName говорит, что это свойство возвращает

null, если текущий экземпляр представляет параметр общего типа, тип массива, тип указателя или тип byref на основе параметра типа или общий тип, который а не определение общего типа, но содержит параметры неразрешенного типа.

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

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication {
  public static class Program {

    public static void Main(string[] args) {
      GenericTypeParameter();
      ArrayType();
      PointerType();
      ByRefTypeBasedOnTypeParameter();
      NongenericTypeDefinitionWithUnresolvedTypeParameters();
      Console.ReadKey();
    }

    public static void GenericTypeParameter() {
      var type = typeof(IEnumerable<>)
        .GetGenericArguments()
        .First();
      PrintFullName("Generic type parameter", type);
    }

    public static void ArrayType() {
      var type = typeof(object[]);
      PrintFullName("Array type", type);
    }

    public static void PointerType() {
      var type = typeof(int*);
      PrintFullName("Pointer type", type);
    }

    public static void ByRefTypeBasedOnTypeParameter() {
      var type = null;
      PrintFullName("ByRef type based on type parameter", type);
    }

    private static void NongenericTypeDefinitionWithUnresolvedTypeParameters() {
      var type = null;
      PrintFullName("Nongeneric type definition with unresolved type parameters", type);
    }

    public static void PrintFullName(string name, Type type) {
      Console.WriteLine(name + ":");
      Console.WriteLine("--Name: " + type.Name);
      Console.WriteLine("--FullName: " + (type.FullName ?? "null"));
      Console.WriteLine();
    }
  }
}

У этого вывода есть.

Generic type parameter:
--Name: T
--FullName: null

Array type:
--Name: Object[]
--FullName: System.Object[]

Pointer type:
--Name: Int32*
--FullName: System.Int32*

ByRef type based on type parameter:
--Name: Program
--FullName: ConsoleApplication.Program

Nongeneric type definition with unresolved type parameters:
--Name: Program
--FullName: ConsoleApplication.Program

Я всего лишь один на пять с двумя "пробелами".

Вопрос

Может ли кто-нибудь изменить мой код, чтобы дать простые примеры каждого способа, в котором Type.FullName может быть нулевым?

Ответ 1

Итак, я сразу заметил, что цитата из MSDN включает "или" два раза в списке дел, но мне потребовалось слишком много времени, чтобы понять, почему. Реальность такова, что есть три основных случая, когда один из этих трех разделен на три следующих случая. Используя более четкую пунктуацию, случаи

  • параметр типового типа;
  • тип массива, тип указателя или byref, основанный на параметре типа; или
  • общий тип, который не является типичным определением типа, но содержит параметры неразрешенного типа.

Я понял первый случай, ответ Рахула направил меня на это сообщение в блоге MSDN, которое объясняет и дает два примера последнего случая, а теперь Я могу привести примеры оставшихся случаев.

using System;
using System.Linq;

namespace ConsoleApplication {

  public class GenericClass<T> {
    public void ArrayMethod(T[] parameter) { }
    public void ReferenceMethod(ref T parameter) { }
  }

  public class AnotherGenericClass<T> : GenericClass<T> { }

  public static class Program {

    public static void Main(string[] args) {
      GenericTypeParameter();
      ArrayTypeBasedOnTypeParameter();
      PointerTypeBasedOnTypeParameter();
      ByRefTypeBasedOnTypeParameter();
      NongenericTypeDefinitionWithUnresolvedTypeParameters();
      Console.ReadKey();
    }

    public static void GenericTypeParameter() {
      var type = typeof(GenericClass<>)
        .GetGenericArguments()
        .First();
      PrintFullName("Generic type parameter", type);
    }

    public static void ArrayTypeBasedOnTypeParameter() {
      var type = typeof(GenericClass<>)
        .GetMethod("ArrayMethod")
        .GetParameters()
        .First()
        .ParameterType;
      PrintFullName("Array type based on type parameter", type);
    }

    /*
     * Would like an actual example of a pointer to a generic type,
     * but this works for now.
     */
    public static void PointerTypeBasedOnTypeParameter() {
      var type = typeof(GenericClass<>)
        .GetGenericArguments()
        .First()
        .MakePointerType();
      PrintFullName("Pointer type based on type parameter", type);
    }

    public static void ByrefTypeBasedOnTypeParameter() {
      var type = typeof(GenericClass<>)
        .GetMethod("ReferenceMethod")
        .GetParameters()
        .First()
        .ParameterType;
      PrintFullName("ByRef type based on type parameter", type);
    }

    private static void NongenericTypeDefinitionWithUnresolvedTypeParameters() {
      var type = typeof(AnotherGenericClass<>).BaseType;
      PrintFullName("Nongeneric type definition with unresolved type parameters", type);
    }

    public static void PrintFullName(string name, Type type) {
      Console.WriteLine(name + ":");
      Console.WriteLine("--Name: " + type.Name);
      Console.WriteLine("--FullName: " + (type.FullName ?? "null"));
      Console.WriteLine();
    }
  }
}

/***Output***
Generic type parameter:
--Name: T
--FullName: null

Array type based on type parameter:
--Name: T[]
--FullName: null

Pointer type based on type parameter:
--Name: T*
--FullName: null

Byref type based on type parameter:
--Name: T&
--FullName: null

Nongeneric type definition with unresolved type parameters:
--Name: GenericClass`1
--FullName: null
***Output***/

Ответ 2

Вы можете проверить этот блог MSDN, который демонстрирует, когда Type.FullName возвращает null.

Например, предположим, что у нас есть сборка, скомпилированная со следующим Код С#:

class G<T> {
    public void M<S>() { } 
}

typeof(G<>).FullName G`1, и мы можем обойти этот тип от Type.GetType(G`1). Но мы можем создавать более сложные общие типы, например G<S> (тип G<>, связанный с общим параметром из метод M<>); для того, чтобы идентифицировать такой тип со строкой, много нужна дополнительная информация.

Ниже приведены примеры, когда Type.FullName возвращает null.

class G<T> {
  public class C { }
  public void M(C arg) { }
}
class G2<T> : G<T> { }

string s1 = typeof(G<>).GetGenericArguments()[0].FullName; 
// T in G<T>: generic parameter
string s2 = typeof(G<>).GetMethod("M").GetParameters()[0].ParameterType.FullName; 
// check out the IL, it is G`1/C<!T> (not generic type definition) 
// Related topic, see this 
string s3 = typeof(G2<>).BaseType.FullName; 
// base type of G2<>, which is not generic type definition either
// it equals to typeof(G<>).MakeGenericType(typeof(G2<>).GetGenericArguments()[0])