클래스 유형을 해결해야하는 방법이 있습니다. 이 클래스는 다음과 유사한 네임 스페이스를 가진 다른 어셈블리에 있습니다.
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");
답변
이 보편적 인 솔루션은 로드 필요로하는 사람을위한 동적 외부 참조에서 제네릭 형식 에 의해을 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();
}
저와 동일한 문제를 가진 사람들을 돕기 위해 솔루션을 공유하고 있습니다 (외부 참조 어셈블리에서 부분적으로 또는 전체적으로 정의 될 수있는 문자열에서 모든 유형을 역 직렬화하고 참조는 앱 사용자가 동적으로 추가 함).
누구에게나 도움이되기를 바랍니다!
답변
OP와 유사하게 이름별로 제한된 유형의 하위 집합을로드해야했습니다 (제 경우에는 모든 클래스가 단일 어셈블리에 있고 동일한 인터페이스를 구현했습니다). Type.GetType(string)
다른 어셈블리에 대해 사용하려고 할 때 이상한 문제가 많이 발생 했습니다 (다른 게시물에서 언급 한대로 AssemblyQualifiedName을 추가하는 경우도 있음). 문제를 해결 한 방법은 다음과 같습니다.
용법:
var mytype = TypeConverter<ICommand>.FromString("CreateCustomer");
암호:
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);
}
}
}
}
분명히 CacheTypes 메서드를 조정하여 AppDomain의 모든 어셈블리 또는 사용 사례에 더 잘 맞는 다른 논리를 검사 할 수 있습니다. 사용 사례가 여러 네임 스페이스에서 형식을로드 할 수 있도록 허용하는 경우 해당 형식을 FullName
대신 사용하도록 사전 키를 변경할 수 있습니다 . 또는 유형이 공통 인터페이스 또는 기본 클래스에서 상속되지 않는 <BaseType>
경우 다음과 같이 사용하도록 CacheTypes 메서드를 제거 하고 변경할 수 있습니다..GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")
답변
먼저 어셈블리를로드 한 다음 유형을로드합니다. 예 : 어셈블리 DLL = Assembly.LoadFile (PATH); DLL.GetType (typeName);
답변
표준 방법 중 하나를 사용할 수 있습니까?
typeof( MyClass );
MyClass c = new MyClass();
c.GetType();
그렇지 않은 경우 어셈블리에 대한 정보를 Type.GetType에 추가해야합니다.
답변
AssemblyQualifiedName
속성을 이용한 짧고 역동적 인 접근 –
Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)
즐겨!