Своя реализация Type.FullName
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 19.06.21 19:36
Оценка:
Хочу соорудить свою реализацию Type.FullName

  Вот мой страшный вариант (пока лучше не смотреть)
 //-----------------------------------------------------------------------
 public static string Extension__BuildHumanName(this Type type)
 {
  Debug.Assert(!type.IsGenericParameter);

  var ctx
   =new tagBuildHumanNameCtx
     (type);

  var result
   =Helper__BuildHumanName
     (ref ctx,
      type);

  return result;
 }//Extension__BuildHumanName

 //-----------------------------------------------------------------------
 private struct tagBuildHumanNameCtx
 {
  public int iGenericArg;

  public readonly Type[] GenericArguments;

  public tagBuildHumanNameCtx(Type type)
  {
   this.iGenericArg=0;

   if(!type.IsGenericType)
    this.GenericArguments=null;
   else
    this.GenericArguments=type.GetGenericArguments();
  }//tagCTX
 };//struct tagBuildHumanNameCtx

 //-----------------------------------------------------------------------
 private static string Helper__BuildHumanName(ref tagBuildHumanNameCtx ctx,
                                              Type                     type)
 {
  Debug.Assert(!Object.ReferenceEquals(type,null));

  var sb
   =new System.Text.StringBuilder();

  //------------------------------------------------------- ARRAY
  if(type.IsArray)
  {
   var elementType
    =type.GetElementType();

   Debug.Assert(!Object.ReferenceEquals(elementType,null));

   sb.Append
    (Extension__BuildHumanName(elementType));

   var arrayRank
    =type.GetArrayRank();

   Debug.Assert(arrayRank>0);

   for(int i=0;i!=arrayRank;++i)
    sb.Append("[]");

   return sb.ToString();
  }//if type.IsArray

  Debug.Assert(!type.IsArray);

  //------------------------------------------------------- STD

  //------------------------- parent class OR namespace
  if(!Object.ReferenceEquals(type.DeclaringType,null))
  {
   var declaringTypeSign
    =Helper__BuildHumanName
      (ref ctx,
       type.DeclaringType);

   Debug.Assert(!string.IsNullOrEmpty(declaringTypeSign));

   sb.Append(declaringTypeSign);
   sb.Append('+');
  }
  else
  {
   for(;;)
   {
    if(Helper__BuildHumanName__SkipNms(type))
     break;

    var nms
     =type.Namespace;

    if(Object.ReferenceEquals(nms,null))
     break;

    Debug.Assert(nms.Length>0);

    if(nms.Length==0)
     break;

    sb.Append(nms);
    sb.Append('.');

    break;
   }//for[ever]
  }//else

  //------------------------- name
  if(!type.IsGenericType)
  {
   sb.Append(Helper__CheckTypeName(type.Name));
  }
  else
  {
   Debug.Assert(type.IsGenericType);

   Debug.Assert(!Object.ReferenceEquals(ctx.GenericArguments,null));
   Debug.Assert(ctx.iGenericArg<=ctx.GenericArguments.Length);

   //-------------------------
   var extractResult
    =Helper__ExtractGenericName(type);

   sb.Append(extractResult.Name);

   if(!extractResult.HasGenericParams)
   {
    //NO GENERIC PARAMETERS
   }
   else
   {
    //HAS GENERIC PARAMETERS

    sb.Append('<');
   
    var cGenericArgs
     =type.GetGenericArguments().Length;
   
    Debug.Assert(cGenericArgs>0);
    Debug.Assert(cGenericArgs<=ctx.GenericArguments.Length);
   
    Debug.Assert(ctx.iGenericArg<=cGenericArgs);
   
    bool isFirst
     =true;
   
    for(;ctx.iGenericArg!=cGenericArgs;++ctx.iGenericArg,isFirst=false)
    {
     Debug.Assert(ctx.iGenericArg<ctx.GenericArguments.Length);
   
     var a
      =ctx.GenericArguments[ctx.iGenericArg];
   
     Debug.Assert(!Object.ReferenceEquals(a,null));
   
     if(!isFirst)
      sb.Append(',');
   
     if(!a.IsGenericParameter)
     {
      if(!isFirst)
       sb.Append(' ');
   
      sb.Append
       (Extension__BuildHumanName(a));
     }//if !a.IsGenericParameter
    }//for i
   
    sb.Append('>');
   }//if extractResult.Item1
  }//else type.IsGenericType with parameters

  //-------------------------- Go home...
  return sb.ToString();
 }//Helper__BuildHumanName

 //-----------------------------------------------------------------------
 private static bool Helper__BuildHumanName__SkipNms(Type type)
 {
  Debug.Assert(!Object.ReferenceEquals(type,null));

  if(!type.IsGenericType)
   return false;

  if(!type.IsGenericTypeDefinition)
   type=type.GetGenericTypeDefinition();

  //special support for Nullable - skip namespace
  if(type==Structure_TypeCache.TypeOf__System_Nullable)
   return true;

  return false;
 }//Helper__BuildHumanName__SkipNms

 //-----------------------------------------------------------------------
 private struct tagResultOfExtractGenericName
 {
  public bool    HasGenericParams;
  public string  Name;

  public tagResultOfExtractGenericName(bool hasGenericParams,string name)
  {
   this.HasGenericParams=hasGenericParams;

   this.Name=name;
  }//tagResultOfExtractGenericName
 };//class tagResultOfExtractGenericName

 //-----------------------------------------------------------------------
 private static tagResultOfExtractGenericName Helper__ExtractGenericName(Type type)
 {
  Debug.Assert(!Object.ReferenceEquals(type,null));
  Debug.Assert(type.IsGenericType);

  //Example: "Nullable`1"
  var name
   =Helper__CheckTypeName(type.Name);

  var i
   =name.IndexOf('`');

  if(i==-1)
   return new tagResultOfExtractGenericName(false,name);

  Debug.Assert(i>0);

  var name2
   =name.Substring(0,i);

  return new tagResultOfExtractGenericName(true,Helper__CheckTypeName(name2));
 }//Helper__ExtractGenericName

 //-----------------------------------------------------------------------
 private static string Helper__CheckTypeName(string name)
 {
  Debug.Assert(!string.IsNullOrEmpty(name));

  if(Object.ReferenceEquals(name,null))
   return "##NULL_TYPE_NAME";

  if(name.Length==0)
   return "##EMPTY_TYPE_NAME";

  return name;
 }//Helper__CheckTypeName

Вот тесты, которые отрабатывают:
 static class tagCLASS09_00
 {
  static public class tagCLASS09_01<T1>
  {
   static public class tagCLASS09_02
   {
    static public class tagCLASS09_03<T2,T3>
    {
     public static T1 EXEC(T1 v)
     {
      return v;
     }//EXEC
    }//class tagCLASS09_03<T2,T3>
   }//class tagCLASS09_02
  }//class tagCLASS09_01<T>
 }//class tagCLASS09_00

 //-----------------------------------------------------------------------
 [Test]
 public static void Test_09__nested_01_spec()
 {
  Assert.AreEqual
   ("TestsFor__Extension__BuildHumanName+tagCLASS09_00+tagCLASS09_01<System.Int32>+tagCLASS09_02+tagCLASS09_03<System.Int16, System.String>",
    typeof(tagCLASS09_00.tagCLASS09_01<int>.tagCLASS09_02.tagCLASS09_03<short,string>).Extension__BuildHumanName());
 }//Test_09__nested_01_spec

 //-----------------------------------------------------------------------
 [Test]
 public static void Test_09__nested_02_gen()
 {
  Assert.AreEqual
   ("TestsFor__Extension__BuildHumanName+tagCLASS09_00+tagCLASS09_01<>+tagCLASS09_02+tagCLASS09_03<,>",
    typeof(tagCLASS09_00.tagCLASS09_01<>.tagCLASS09_02.tagCLASS09_03<,>).Extension__BuildHumanName());
 }//Test_09__nested_02_gen


Проблема с вложенным классом tagCLASS09_02. Он generic, но без параметров.

Как мне это (то, что он без параметров) по-человечески определить через свойства System.Type?

Сейчас я смотрю на Type.Name и если в нем есть символ '`', то значит параметры есть, а если нет — значит нет.

Этот изврат выполняется в методе Helper__ExtractGenericName.

Но это как-то через .... пятую точку.

Трассировка работы тестов дает такие данные:

Test_09__nested_01_spec:
type.Name: "tagCLASS09_01`1" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1
type.Name: "tagCLASS09_02" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1
type.Name: "tagCLASS09_03`2" type.GenericTypeArguments: 3 type.GenericTypeParameters: 0

Test_09__nested_02_gen:
type.Name: "tagCLASS09_01`1" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1
type.Name: "tagCLASS09_02" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1
type.Name: "tagCLASS09_03`2" type.GenericTypeArguments: 0 type.GenericTypeParameters: 3


Не понятно, на что тут, кроме апострофа в Type.Name, можно опереться.

Не подскажите, где можно украсть правильное решение?

--- [пытался копать реализацию Type.Name]
Отладчик показывает RuntimeType.CoreCLR.cs

        public override string Name => GetCachedName(TypeNameKind.Name)!;

        // This method looks like an attractive inline but expands to two calls,
        // neither of which can be inlined or optimized further. So block it
        // from inlining.
        [MethodImpl(MethodImplOptions.NoInlining)]
        private string? GetCachedName(TypeNameKind kind) => Cache.GetName(kind);


Ну а дальше там дебри...
private string ConstructName([NotNull] ref string? name, TypeNameFormatFlags formatFlags) =>
 name ??= new RuntimeTypeHandle(m_runtimeType).ConstructName(formatFlags);


[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void ConstructName(QCallTypeHandle handle, TypeNameFormatFlags formatFlags, StringHandleOnStack retString);
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Своя реализация Type.FullName
От: Danchik Украина  
Дата: 19.06.21 20:26
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>Не подскажите, где можно украсть правильное решение?


https://github.com/linq2db/linq2db/blob/master/Source/LinqToDB/Common/Internal/TypeExtensions.cs#L39
Re[2]: Своя реализация Type.FullName
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 20.06.21 03:56
Оценка:
Здравствуйте, Danchik, Вы писали:

КД>>Не подскажите, где можно украсть правильное решение?


D>https://github.com/linq2db/linq2db/blob/master/Source/LinqToDB/Common/Internal/TypeExtensions.cs#L39


var genericPartIndex = type.Name.IndexOf('`');
if (genericPartIndex <= 0)
{
    builder.Append(type.Name);
    return;
}


Не, ну это как у меня. Не честно интересно.

Кста, меня терзают смутные сомнения насчет правильности обработки нулевого значения.

---
Я тут ночью проснулся и подумал — "дурень я дурень, надо было другие данные смотреть"

Test_09__nested_01_spec:
type.Name: "tagCLASS09_01`1" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1 type.GetGenericArguments: 1
type.Name: "tagCLASS09_02" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1 type.GetGenericArguments: 1
type.Name: "tagCLASS09_03`2" type.GenericTypeArguments: 3 type.GenericTypeParameters: 0 type.GetGenericArguments: 3

Test_09__nested_02_gen:
type.Name: "tagCLASS09_01`1" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1 type.GetGenericArguments: 1
type.Name: "tagCLASS09_02" type.GenericTypeArguments: 0 type.GenericTypeParameters: 1 type.GetGenericArguments: 1
type.Name: "tagCLASS09_03`2" type.GenericTypeArguments: 0 type.GenericTypeParameters: 3 type.GetGenericArguments: 3


Смотрим сверху вниз — от корневого класса к вложенному.

Количество параметров generic-типа — это разница текущего и предыдущего значения GetGenericArguments().Length.

Или, другими словами this.GetType().GetGenericArguments().Length — this.DeclaringType().GetGenericArguments().Length

Это для этого, конкретного случая, когда нет наследования generic-интерфейсов.

А если еще наследуются generic-интерфейсы, то наверное надо учитывать GetGenericArguments().Length реализуемых интерфейсов.

Там наверное надо выполнять обход реализуемых интерфейсов, чтобы правильно вычислять индекс первого аргумента (offset) в type.GetGenericArguments() — tagBuildHumanNameCtx.iGenericArg.

UPD

Это я не туда стал думать. Интерфейсы наследуются, а тут у нас вложенные конструкции.

Так что тут все норм.

Идея с "разница текущего и предыдущего значения GetGenericArguments().Length" работает на ура.

Там правда ничего вычитать не надо. Надо просто запоминать сколько generic параметров у охватывающего класса. Если оно равно текущему — значит generic параметров нет.

---
Все перерыл — не нашел где в Type можно взять "чистое" имя generic типа без этого '`'. Походу такого свойства нет.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Отредактировано 21.06.2021 20:01 DDDX . Предыдущая версия . Еще …
Отредактировано 21.06.2021 19:47 DDDX . Предыдущая версия .
Re: Своя реализация Type.FullName
От: Kolesiki  
Дата: 20.06.21 18:00
Оценка: :)
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>[cut=Вот мой страшный вариант (пока лучше не смотреть)]


PasteBin'у не учили в детстве? ЗДЕСЬ эта простыня зачем?

КД>Вот тесты, которые отрабатывают:


КД> static public class tagCLASS09_02


КД>Проблема с вложенным классом tagCLASS09_02. Он generic, но без параметров.


Вот здесь вообще не понял. Что в нём "generic", если нет параметров??
Re[2]: Своя реализация Type.FullName
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.06.21 07:31
Оценка:
Здравствуйте, Kolesiki, Вы писали:
K>Вот здесь вообще не понял. Что в нём "generic", если нет параметров??
public class Tree<T>
{
  public class Node
  {}
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Своя реализация Type.FullName
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 30.06.21 12:30
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

Посмотри давно писал .Net Core, AppDomain, WCF, RPC маршалинг по Tcp/Ip свой велосипед
Может, что и пригодится
и солнце б утром не вставало, когда бы не было меня
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.