Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

As a followup to this question I have now come to the problem of being able to get the Type of a type that is defined by the user in his own solution. Using the standard mscorlib types, everything works.

The question is very easy: how can I get this type from an assembly that I will only know at runtime?

As described in the comments here:

Also, what do you mean by "extracting the type"? You mean getting the Reflection type? There's no good helper, partly because (typically) you can never assume the type you're compiling against is creatable at runtime. There's a strong (and often overlooked) distinction between "compile time" and "run time", and it's rare to bridge them.

Or here on the previous question:

Well, so getting a Type for TypeInfo, naming issues aside, is a tricky problem. It assumes you have an assembly that can be loaded and found. When you do a build, the compiler might be loading reference assemblies that themselves can't be loaded as "normal" assemblies. Even if they are, you might have to hook AppDomain.AssemblyResolve to locate your references, and whatever assembly you built.

"Build" and "runtime" are really different domains, and crossing from one to the other is poorly defined, at best. I assume here that you really need a System.Type because you're using some other reflection API, or trying to then load that type and execute code from it.

I have followed the approach as laid out here and implemented it as such in my Analyzer:

private static Dictionary<string, Assembly> _assemblies = new Dictionary<string, Assembly>();

var containingAssembly = semanticModel.GetSymbolInfo(argument)
                                      .Symbol
                                      .ContainingAssembly;

if (!_assemblies.TryGetValue(containingAssembly.ToString(), out Assembly result))
{
    var newAssembly = Assembly.Load(containingAssembly.ToString());
    _assemblies.Add(containingAssembly.ToString(), newAssembly);
}

var currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += ResolveAssemblies;

private Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
    _assemblies.TryGetValue(args.Name, out Assembly result);
    return result;
}

But this hasn't made a difference, I keep getting

The User Diagnostic Analyzer 'DiagnosticTools.Collections.ElementaryMethodsNotOverriden.ElementaryMethodsNotOverridenAnalyzer' threw an exception with message 'Could not load file or assembly 'RoslynTester, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.'.

Using fuslogvw.exe gives me this log information which boils down to

LOG: All probing URLs attempted and failed.

After searching for the .dll and .exe version in a few subfolders of /Common7/IDE/.

As context to clarify why I'm doing this: I want to check every type that is used in a collection and verify that it overrides both Equals and GetHashCode. To determine this I have a "classic" reflection extension method that checks this for me:

public static bool IsOverridden(this MethodInfo method)
{
    return method.GetBaseDefinition().DeclaringType != method.DeclaringType;
}

So should Roslyn have a way to verify this itself that would make it so that I don't have to use classic reflection at all, then this would also be fine.

Update:

When I use this code as provided by MSDN, I get an "invalid parameter" exception inside Visual Studio but fuslogvw still shows a "file not found" error message. What causes this discrepancy?

private Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
    Assembly MyAssembly, objExecutingAssemblies;
    string strTempAssmbPath = "";

    objExecutingAssemblies = Assembly.GetExecutingAssembly();
    AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();

    foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
    {
        if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
        {               
            strTempAssmbPath = @"C:\Users\Jeroen\Documents\Visual Studio 2013\Projects\RoslynTester\RoslynTester\bin\Debug\" + args.Name.Substring(0, args.Name.IndexOf(",")) + ".exe";
            break;
        }
    }                   
    MyAssembly = Assembly.LoadFrom(strTempAssmbPath);

    return MyAssembly;
}
share|improve this question
    
It is a "file not found" error, the most common error there is. It is very unclear why you think the CLR should be able to locate the file. If it is not in the GAC and not in the probing path then that's the end of it, Fuslogvw showed you where it looked. Your AssemblyResolve event doesn't accomplish anything, it can only find assemblies that have been loaded before and the CLR already knows how to do that by itself. –  Hans Passant Apr 27 at 15:37
    
I have looked into these concepts but I have a few questions left. I don't think the GAC is a solution considering that would force the user to add his assemblies to it manually. Furthermore: the RoslynTester project is simply a Console Application and the diagnostic information (which relies on getting the type from that assembly) should be available before building the project. How can I point ResolveAssemblies to the new assembly when it is unknown to me at compiletime in what directory to find it in? I have updated the ResolveAssemblies code. –  Jeroen Vannevel Apr 27 at 16:52
add comment

1 Answer 1

up vote 2 down vote accepted

I'm assuming you've already found out (programmatically) which class are contained in your collection. You don't really need reflection to accomplish what you want. With Roslyn you can check if a class overrides the Equals method with this SyntaxWalker:

public class FindOverrides : CSharpSyntaxWalker
{
    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        base.VisitMethodDeclaration(node);

        if (node.Identifier.Text == "Equals" 
            && node.Modifiers.Any(m => m.Text == "override"))
        {
            // found an override of Equals()    
        }
    }
}

To (blindly) check every method of every type in a given solution it can be used like this:

var syntaxRoots =
    from project in solution.Projects
    from document in project.Documents
    select document.GetSyntaxRootAsync().Result;

foreach (var root in syntaxRoots)
    new FindOverrides().Visit(root);

There is at least one omission (if that's a valid case for you): my code above won't find if a base class of a given type is overriding Equals().

share|improve this answer
    
This approach looks very promising, thanks for pointing me towards it! After implementing your code I get One or more errors occurred exceptions and when I turn on the AggregateException I see that the stacktrace leads deep into System.Threading. Have I overlooked something in my implementation that could cause this? –  Jeroen Vannevel Apr 27 at 17:45
    
Hi, glad I could help. I couldn't immediately see a problem by reading your gist. Please have a look at the InnerException(s) of the AggregateException you encounter. If that doesn't help you to fix it, please post the exceptions together with a stack trace and the (minimal) piece of code that causes them. –  andyp Apr 27 at 18:26
    
I managed to resolve it (although I forgot what it was exactly, my code went through 3 refactors since). I have entertained the idea of the SyntaxWalker but eventually found out that it wasn't needed: TypeInfo.Type.GetMembers() contains all the members (and thus methods) declared on that level and has an explicit .IsOverride property. So I pretty much threw away a big bunch of code and replaced it with these few lines. From the initial checks it seems to work just fine, I'll add a CW answer tomorrow after I had the chance to look it all through. I appreciate your input! –  Jeroen Vannevel Apr 27 at 21:23
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.