Właściwości nazwy klasy System.Type zwracają dziwny wynik w przypadku typów ogólnych. Czy istnieje sposób na uzyskanie nazwy typu w bliższym formacie, jaki określiłem? Przykład: typeof(List<string>).OriginalName == "List<string>"
C#: Funkcja nazwy typu "Pretty"?
Odpowiedz
Zrozumiałem, że muszę sam to napisać. Oto moje rozwiązanie (w rzeczywistości jest to trochę więcej niż wymagano). Jest to prawdopodobnie pomocne.
using System.Reflection;
using HWClassLibrary.Debug;
using System.Collections.Generic;
using System.Linq;
using System;
namespace HWClassLibrary.Helper
{
public static class TypeNameExtender
{
private static IEnumerable<Type> _referencedTypesCache;
public static void OnModuleLoaded() { _referencedTypesCache = null; }
public static string PrettyName(this Type type)
{
if(type == typeof(int))
return "int";
if(type == typeof(string))
return "string";
var result = PrettyTypeName(type);
if(type.IsGenericType)
result = result + PrettyNameForGeneric(type.GetGenericArguments());
return result;
}
private static string PrettyTypeName(Type type)
{
var result = type.Name;
if(type.IsGenericType)
result = result.Remove(result.IndexOf('`'));
if (type.IsNested && !type.IsGenericParameter)
return type.DeclaringType.PrettyName() + "." + result;
if(type.Namespace == null)
return result;
var conflictingTypes = ReferencedTypes
.Where(definedType => definedType.Name == type.Name && definedType.Namespace != type.Namespace)
.ToArray();
var namespaceParts = type.Namespace.Split('.').Reverse().ToArray();
var namespacePart = "";
for(var i = 0; i < namespaceParts.Length && conflictingTypes.Length > 0; i++)
{
namespacePart = namespaceParts[i] + "." + namespacePart;
conflictingTypes = conflictingTypes
.Where(conflictingType => (conflictingType.Namespace + ".").EndsWith(namespacePart))
.ToArray();
}
return namespacePart + result;
}
private static IEnumerable<Type> ReferencedTypes
{
get
{
if(_referencedTypesCache == null)
_referencedTypesCache = Assembly.GetEntryAssembly().GetReferencedTypes();
return _referencedTypesCache;
}
}
private static string PrettyNameForGeneric(Type[] types)
{
var result = "";
var delim = "<";
foreach(var t in types)
{
result += delim;
delim = ",";
result += t.PrettyName();
}
return result + ">";
}
}
}
Musisz napisać to sam. Należy pamiętać, że Type.Name
itd. Wywołują metody, które znajdują się w CLR i mogą być wywoływane z wielu języków. To dlatego nie wracają, wyglądając jak C# lub VB, lub język, w którym był kodowany wywołujący, ale zamiast tego wygląda jak reprezentacja CLR.
Należy pamiętać, że string
i co nie są aliasy dla typów CLR, takich jak System.String
. Ponownie, odgrywa to rolę w formatowaniu, które widzisz.
Nie jest trudno zrobić, używając refleksji, ale kwestionuję jej wartość.
Chciałbym wiedzieć, dlaczego to zostało odrzucone. – jason
jak w przykładzie można oczekiwać typ więc można spróbować
public class test<T> where T : class
{
public List<String> tt
{
get;
set;
}
}
///////////////////////////
test<List<String>> tt = new test<List<String>>();
if(tt.GetType().FullName.Contains(TypeOf(List<String>).FullName))
{
//do something
}
else
{
//do something else
}
Problem z „czystych” imion to są one różne w zależności od języka, którego używasz. Wyobraźmy sobie niespodziankę programisty VB.NET, jeśli OriginalName
zwróci składnię C#.
Jednak jest to dość dość łatwo zrobić to samemu:
private static string PrettyName(Type type)
{
if (type.GetGenericArguments().Length == 0)
{
return type.Name;
}
var genericArguments = type.GetGenericArguments();
var typeDefeninition = type.Name;
var unmangledName = typeDefeninition.Substring(0, typeDefeninition.IndexOf("`"));
return unmangledName + "<" + String.Join(",", genericArguments.Select(PrettyName)) + ">";
}
To będzie rekursywnie rozwiązać nazwę niezarządzanej, tak, że jeśli masz coś jak Dictionary<string, IList<string>>
powinna nadal działać.
Idealnie wymaga to dalszego rozwoju do obsługi typów takich jak 'Dictionary
użyłem CodeDomProvider
przekonwertować do C#:
public static string GetOriginalName(this Type type)
{
string TypeName = type.FullName.Replace(type.Namespace + ".", "");//Removing the namespace
var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); //You can also use "VisualBasic"
var reference = new System.CodeDom.CodeTypeReference(TypeName);
return provider.GetTypeOutput(reference);
}
Co jeśli chciałbym pominąć przestrzeń nazw? – svick
Można użyć type.Name zamiast type.FullName Myślę, że nie musisz samodzielnie usuwać obszaru nazw. Przepraszamy, teraz widzę, że nie otrzymujesz ogólnych parametrów w ten sposób. – riezebosch
Rozumiem, że chcesz porównać typy.
Najlepszym sposobem na to jest ...
myVar is List<string>
lub
myVar.GetType() == myOtherVar.GetType()
Jeśli nie potrzebujesz tego ... proszę zignorować moją odpowiedź.
jak odpowiedź Harolda Hoyer, ale w tym nullables i jeszcze kilka wbudowanych typów:
/// <summary>
/// Get full type name with full namespace names
/// </summary>
/// <param name="type">
/// The type to get the C# name for (may be a generic type or a nullable type).
/// </param>
/// <returns>
/// Full type name, fully qualified namespaces
/// </returns>
public static string CSharpName(this Type type)
{
Type nullableType = Nullable.GetUnderlyingType(type);
string nullableText;
if (nullableType != null)
{
type = nullableType;
nullableText = "?";
}
else
{
nullableText = string.Empty;
}
if (type.IsGenericType)
{
return string.Format(
"{0}<{1}>{2}",
type.Name.Substring(0, type.Name.IndexOf('`')),
string.Join(", ", type.GetGenericArguments().Select(ga => ga.CSharpName())),
nullableText);
}
switch (type.Name)
{
case "String":
return "string";
case "Int32":
return "int" + nullableText;
case "Decimal":
return "decimal" + nullableText;
case "Object":
return "object" + nullableText;
case "Void":
return "void" + nullableText;
default:
return (string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName) + nullableText;
}
}
pierwszy: Uznanie dla Navid dla unikania reinvention koła. Chciałbym przegłosować, gdybym mógł.
Oto kilka punktów do dodania, jeśli ktokolwiek podąża tą ścieżką (przynajmniej dla VS10/.Net 4):
* Spróbuj użyć jednego z wariantów CodeTypeReference z argumentami typu. Pozwala to uniknąć wielu pułapek używania nazw typów ciągów (takich jak końcowe: &) i oznacza, że odzyskasz bool
zamiast System.Boolean
itd. Otrzymujesz pełny obszar nazw dla wielu typów takich jak ten, ale zawsze możesz się pozbyć część przestrzeni nazw później.
* Simple Nullables wydają się wracać w postaci System.Nullable<int>
zamiast int?
- Można konwertować do ładniejszy składni z regex na odpowiedź, takich jak: const string NullablePattern = @"System.Nullable<(?<nulledType>[\w\.]+)>"; const string NullableReplacement = @"${nulledType}?"; answer = Regex.Replace(answer, NullablePattern, NullableReplacement);
* rodzaj metody argument jak out T?
daje bardzo nieprzejrzysty ciąg . Jeśli ktoś ma elegancki sposób radzenia sobie z takimi rzeczami, chciałbym o tym wiedzieć.
Mam nadzieję, że wszystko to stanie się bardzo łatwe z Roslyn.
Przepraszam, wzór Nullable został zaszyfrowany. Powinien być @"System\.Nullable<(?
–
Oto moja implementacja. Został stworzony do opisywania metod, więc obsługuje słowa kluczowe: ref
i out
.
private static Dictionary<Type, string> shorthandMap = new Dictionary<Type, string>
{
{ typeof(Boolean), "bool" },
{ typeof(Byte), "byte" },
{ typeof(Char), "char" },
{ typeof(Decimal), "decimal" },
{ typeof(Double), "double" },
{ typeof(Single), "float" },
{ typeof(Int32), "int" },
{ typeof(Int64), "long" },
{ typeof(SByte), "sbyte" },
{ typeof(Int16), "short" },
{ typeof(String), "string" },
{ typeof(UInt32), "uint" },
{ typeof(UInt64), "ulong" },
{ typeof(UInt16), "ushort" },
};
private static string CSharpTypeName(Type type, bool isOut = false)
{
if (type.IsByRef)
{
return String.Format("{0} {1}", isOut ? "out" : "ref", CSharpTypeName(type.GetElementType()));
}
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return String.Format("{0}?", CSharpTypeName(Nullable.GetUnderlyingType(type)));
}
else
{
return String.Format("{0}<{1}>", type.Name.Split('`')[0],
String.Join(", ", type.GenericTypeArguments.Select(a => CSharpTypeName(a)).ToArray()));
}
}
if (type.IsArray)
{
return String.Format("{0}[]", CSharpTypeName(type.GetElementType()));
}
return shorthandMap.ContainsKey(type) ? shorthandMap[type] : type.Name;
}
kod Powołanie wygląda następująco:
string line = String.Format("{0}.{1}({2})",
method.DeclaringType.Name,
method.Name,
String.Join(", ", method.GetParameters().Select(p => CSharpTypeName(p.ParameterType, p.IsOut) + " " + p.Name).ToArray()));
Gdzie method
jest wystąpienie MethodInfo
.
Jedna uwaga: nie musiałem opisywać żadnych typów tablic wielowymiarowych, więc nie zawracałem sobie głowy zaimplementowaniem tego opisu, ale byłoby całkiem łatwo dodać, dzwoniąc pod numer type.GetArrayRank()
.
Minimalne rozwiązanie do pracy, które wykorzystuje CodeDomProvider
, to kontrola sposobu budowania instancji CodeTypeReference
w pierwszej kolejności. Istnieją specjalne przypadki tylko dla typów generycznych i tablic multi-Rank, więc mamy tylko dbać o tych:
static CodeTypeReference CreateTypeReference(Type type)
{
var typeName = (type.IsPrimitive || type == typeof(string)) ? type.FullName : type.Name;
var reference = new CodeTypeReference(typeName);
if (type.IsArray)
{
reference.ArrayElementType = CreateTypeReference(type.GetElementType());
reference.ArrayRank = type.GetArrayRank();
}
if (type.IsGenericType)
{
foreach (var argument in type.GetGenericArguments())
{
reference.TypeArguments.Add(CreateTypeReference(argument));
}
}
return reference;
}
Użycie tej zmodyfikowanej metody fabryki, to wtedy można użyć odpowiedniego dostawcy kodu, aby uzyskać ładny pisania , tak jak:
using (var provider = new CSharpCodeProvider())
{
var reference = CreateTypeReference(typeof(IObservable<IEnumerable<Tuple<int?, string>>>[,]));
var output = provider.GetTypeOutput(reference);
Console.WriteLine(output);
}
Powyższy kod zwraca IObservable<IEnumerable<Tuple<Nullable<int>, string>>>[,]
. Jedyny szczególny przypadek, który nie jest dobrze obsługiwany, to typy Nullable
, ale jest to bardziej błędem niż w jakimkolwiek innym przypadku.
To dość łatwo napisać siebie jako metodę rozszerzenia przy użyciu rekursji. –
Taka funkcja nie jest w ramach, ponieważ zależy to od używanego języka: VB ma inną reprezentację generycznych i jestem pewien, że również inne języki. – svick