다른 어셈블리의 클래스 이름에서 유형 확인
클래스 유형을 해결해야하는 방법이 있습니다. 이 클래스는 다음과 유사한 네임 스페이스를 가진 다른 어셈블리에 있습니다.
MyProject.Domain.Model
다음을 수행하려고합니다.
Type.GetType("MyProject.Domain.Model." + myClassName);
이 작업을 수행하는 코드가 확인하려는 유형의 클래스와 동일한 어셈블리에 있으면 잘 작동하지만 클래스가 다른 어셈블리에 있으면이 코드가 실패합니다.
이 작업을 수행하는 훨씬 더 나은 방법이 있다고 확신하지만, 내가 찾고있는 클래스 유형을 해결하기 위해 어셈블리를 해결하고 네임 스페이스를 탐색하는 데 많은 경험이 없습니다. 이 작업을보다 우아하게 수행하기위한 조언이나 팁이 있습니까?
다음과 같이 어셈블리 이름을 추가해야합니다.
Type.GetType("MyProject.Domain.Model." + myClassName + ", AssemblyName");
모호함을 방지하거나 어셈블리가 GAC에있는 경우 다음과 같이 정규화 된 어셈블리 이름을 제공해야합니다.
Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
먼저 어셈블리를로드 한 다음 유형을로드합니다. 예 : 어셈블리 DLL = Assembly.LoadFile (PATH); DLL.GetType (typeName);
AssemblyQualifiedName
속성을 이용한 짧고 역동적 인 접근 -
Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)
즐겨!
이 보편적 인 솔루션은 로드 필요로하는 사람을위한 동적 외부 참조에서 제네릭 형식 에 의해을 AssemblyQualifiedName
있는 조립 제네릭 형식의 모든 부분에서 오는 모르게 :
public static Type ReconstructType(string assemblyQualifiedName, bool throwOnError = true, params Assembly[] referencedAssemblies)
{
foreach (Assembly asm in referencedAssemblies)
{
var fullNameWithoutAssemblyName = assemblyQualifiedName.Replace($", {asm.FullName}", "");
var type = asm.GetType(fullNameWithoutAssemblyName, throwOnError: false);
if (type != null) return type;
}
if (assemblyQualifiedName.Contains("[["))
{
Type type = ConstructGenericType(assemblyQualifiedName, throwOnError);
if (type != null)
return type;
}
else
{
Type type = Type.GetType(assemblyQualifiedName, false);
if (type != null)
return type;
}
if (throwOnError)
throw new Exception($"The type \"{assemblyQualifiedName}\" cannot be found in referenced assemblies.");
else
return null;
}
private static Type ConstructGenericType(string assemblyQualifiedName, bool throwOnError = true)
{
Regex regex = new Regex(@"^(?<name>\w+(\.\w+)*)`(?<count>\d)\[(?<subtypes>\[.*\])\](, (?<assembly>\w+(\.\w+)*)[\w\s,=\.]+)$?", RegexOptions.Singleline | RegexOptions.ExplicitCapture);
Match match = regex.Match(assemblyQualifiedName);
if (!match.Success)
if (!throwOnError) return null;
else throw new Exception($"Unable to parse the type's assembly qualified name: {assemblyQualifiedName}");
string typeName = match.Groups["name"].Value;
int n = int.Parse(match.Groups["count"].Value);
string asmName = match.Groups["assembly"].Value;
string subtypes = match.Groups["subtypes"].Value;
typeName = typeName + $"`{n}";
Type genericType = ReconstructType(typeName, throwOnError);
if (genericType == null) return null;
List<string> typeNames = new List<string>();
int ofs = 0;
while (ofs < subtypes.Length && subtypes[ofs] == '[')
{
int end = ofs, level = 0;
do
{
switch (subtypes[end++])
{
case '[': level++; break;
case ']': level--; break;
}
} while (level > 0 && end < subtypes.Length);
if (level == 0)
{
typeNames.Add(subtypes.Substring(ofs + 1, end - ofs - 2));
if (end < subtypes.Length && subtypes[end] == ',')
end++;
}
ofs = end;
n--; // just for checking the count
}
if (n != 0)
// This shouldn't ever happen!
throw new Exception("Generic type argument count mismatch! Type name: " + assemblyQualifiedName);
Type[] types = new Type[typeNames.Count];
for (int i = 0; i < types.Length; i++)
{
try
{
types[i] = ReconstructType(typeNames[i], throwOnError);
if (types[i] == null) // if throwOnError, should not reach this point if couldn't create the type
return null;
}
catch (Exception ex)
{
throw new Exception($"Unable to reconstruct generic type. Failed on creating the type argument {(i + 1)}: {typeNames[i]}. Error message: {ex.Message}");
}
}
Type resultType = genericType.MakeGenericType(types);
return resultType;
}
그리고 당신은이 코드를 테스트 할 수 있습니다 (콘솔 응용 프로그램) :
static void Main(string[] args)
{
Type t1 = typeof(Task<Dictionary<int, Dictionary<string, int?>>>);
string name = t1.AssemblyQualifiedName;
Console.WriteLine("Type: " + name);
// Result: System.Threading.Tasks.Task`1[[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Type t2 = ReconstructType(name);
bool ok = t1 == t2;
Console.WriteLine("\r\nLocal type test OK: " + ok);
Assembly asmRef = Assembly.ReflectionOnlyLoad("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
// Task<DialogResult> in refTypeTest below:
string refTypeTest = "System.Threading.Tasks.Task`1[[System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
Type t3 = ReconstructType(refTypeTest, true, asmRef);
Console.WriteLine("External type test OK: " + (t3.AssemblyQualifiedName == refTypeTest));
// Getting an external non-generic type directly from references:
Type t4 = ReconstructType("System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", true, asmRef);
Console.ReadLine();
}
I'm sharing my solution to help people with the same problem as me (to deserialize ANY type from string that could be defined both partially or as a whole in externally referenced assembly - and the references are dynamically added by app's user).
Hope it helps anyone!
Similar to the OP, I needed to load a limited subset of types by name (in my case all of the classes were in a single assembly and implemented the same interface). I had a lot of weird issues when trying to use Type.GetType(string)
against a different assembly (even adding the AssemblyQualifiedName as mentioned in other posts). Here is how I solved the issue:
Usage:
var mytype = TypeConverter<ICommand>.FromString("CreateCustomer");
Code:
public class TypeConverter<BaseType>
{
private static Dictionary<string, Type> _types;
private static object _lock = new object();
public static Type FromString(string typeName)
{
if (_types == null) CacheTypes();
if (_types.ContainsKey(typeName))
{
return _types[typeName];
}
else
{
return null;
}
}
private static void CacheTypes()
{
lock (_lock)
{
if (_types == null)
{
// Initialize the myTypes list.
var baseType = typeof(BaseType);
var typeAssembly = baseType.Assembly;
var types = typeAssembly.GetTypes().Where(t =>
t.IsClass &&
!t.IsAbstract &&
baseType.IsAssignableFrom(t));
_types = types.ToDictionary(t => t.Name);
}
}
}
}
Obviously you could tweak the CacheTypes method to inspect all assemblies in the AppDomain, or other logic that better fits your use-case. If your use-case allows for types to be loaded from multiple namespaces, you might want to change the dictionary key to use the type's FullName
instead. Or if your types don't inherit from a common interface or base class, you could remove the <BaseType>
and change the CacheTypes method to use something like .GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")
Can you use either of the standard ways?
typeof( MyClass );
MyClass c = new MyClass();
c.GetType();
If not, you will have to add information to the Type.GetType about the assembly.
참고URL : https://stackoverflow.com/questions/3512319/resolve-type-from-class-name-in-a-different-assembly
'Development Tip' 카테고리의 다른 글
연결 문자열이 유효한지 확인하는 방법은 무엇입니까? (0) | 2020.10.23 |
---|---|
데이터베이스 대 플랫 파일 (0) | 2020.10.23 |
EJB 3.1 @LocalBean 대 주석 없음 (0) | 2020.10.23 |
babel CLI 복사 nonjs 파일 (0) | 2020.10.23 |
npm WARN notsup 건너 뛰기 선택적 종속성 : fsevents@1.0.14에 대해 지원되지 않는 플랫폼 (0) | 2020.10.23 |