12

Is there any way to get a List<string> which contains all 'usings' within a namespace/class?

For instance

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Linq.Dynamic;
using System.Text.RegularExpressions;
using System.Reflection;
namespace MyNamespace.Other.Scripting
{

I would have a List with "System","System.Text", "System.Reflection", and so on.

EDIT: As a commented below, I'm using this http://kamimucode.com/Home.aspx/C-sharp-Eval/1 - C# evaluator, and I have to feed the TypeParser with a list of namespaces. The thing is, I won't always know which ones to put. I'm working on a simple scripting language in C# and from it I'll be able to call C# code aswell. My other alternative is create a keyword such as "using" in the scripting language, but I really don't want to do that. I want to be able to pick an C# Object, do whatever I want with it through my script.txt, and be free to use its referenced namespaces.

I want to use it in my Xna Game engine, to be able to write custom scripts which can execute C# along with my simple script functions.

Ryan Lundy
  • 204,559
  • 37
  • 180
  • 211
Conrad Clark
  • 4,533
  • 5
  • 45
  • 70
  • 1
    Do you want this for a source file (i.e.: an actual .cs file read from disk) or do you want to know which namespaces a given compiled type actually references? I'm guessing the latter since you mention reflection, but could you please confirm? – Nicole Calinoiu Apr 28 '11 at 16:00
  • 'which namespaces a given compiled type references' would be my choice – Conrad Clark Apr 28 '11 at 16:06
  • 1
    I'm guessing not on this, because the usings are actually just shorthand for the compiler on non-qualified types. I'll check though. – James Michael Hare Apr 28 '11 at 16:09
  • 2
    BTW, why? I'm not sure I can see the direct need for this in an application unless you're wanting to apply some standards? If that's the case, you could create custom StyleCop assemblies to analyze them? – James Michael Hare Apr 28 '11 at 16:09
  • 1
    @Conrad: That's certainly doable, but it won't have anything to do with the using directives that may or may not have been present in the original code file. Is that OK? Also, I'm with James in wondering what you want to do with the results (partly because it affects implementation choices). – Nicole Calinoiu Apr 28 '11 at 16:17
  • I'm using this http://kamimucode.com/Home.aspx/C-sharp-Eval/1 - C# evaluator, and I have to feed the TypeParser with a list of namespaces. The thing is, I won't always know which ones to put. I'm working on a simple scripting language in C# and from it I'll be able to call C# code aswell. My other alternative is create a keyword such as "using" in the scripting language, but I really don't want to do that. I want to be able to pick an C# Object, do whatever I want with it through my script.txt, and be free to use its referenced namespaces. – Conrad Clark Apr 28 '11 at 16:39
  • I guess one way would be `File.ReadAllLines(yourSourceFile)`, then use LINQ to select just the ones starting with "using " but not with "using (". Then cut off the "using " and any aliases in front and the ; on the end, and then you've got your list. – Ryan Lundy Apr 28 '11 at 17:06
  • @Kyralessa: I won't have the files. Just the binaries. I want to get each namespace referenced inside a class. – Conrad Clark Apr 28 '11 at 17:18

3 Answers3

5

This will work for all types in methods of the declaring class, however it wont give all namespaces for all classes in the file where it was before compiling. That is impossible because after compilation the framework cannot know what was where in files.

So if you have one CLASS per file this will work: If you are missing something (i look for fields and methods, maybe something is not taken in account, if that is so just add)

List<string> namespaces = new List<string>();

        var m = MethodInfo.GetCurrentMethod();

            foreach (var mb in m.DeclaringType.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
            {
                if (mb.MemberType == MemberTypes.Method && ((MethodBase)mb).GetMethodBody() != null)
                {

                    foreach (var p in ((MethodInfo)mb).GetMethodBody().LocalVariables)
                    {
                        if (!namespaces.Contains(p.LocalType.Namespace))
                        {
                            namespaces.Add(p.LocalType.Namespace);
                            Console.WriteLine(p.LocalType.Namespace);
                        }
                    }
                }
                else if (mb.MemberType == MemberTypes.Field) {
                    string ns = ((System.Reflection.FieldInfo)mb).FieldType.Namespace;
                    if (!namespaces.Contains(ns))
                    {                        
                        namespaces.Add(ns);
                        Console.WriteLine(ns);
                    }
                }
            }

Sample output for my case:

System
System.Collections.Generic
System.Reflection
WindowsFormsApplication2
System.Linq.Expressions
Marino Šimić
  • 7,318
  • 1
  • 31
  • 61
  • Thanks alot. That partially solved my problem, but it's more than enough. I just tested adding various objects to a `Stack` through my scripts and it worked perfectly. – Conrad Clark Apr 29 '11 at 13:58
1

Here's a rough untested attempt to get you started:

IEnumerable<string> GetNamespacesUsed(string fileName)
{
    var lines = System.IO.File.ReadAllLines(fileName);
    var usingLines = lines.Where(
        x => x.StartsWith("using ") && !x.StartsWith("using ("));

    foreach (var line in usingLines)
    {
        if (line.Contains("="))
            yield return line.Substring(line.IndexOf("="), line.Length - 1);
        else
            yield return line.Substring(line.Length - 1);
    }
}

The line.Length - 1 may not be correct to cut off the semicolon on the end. You'll need to test it to find out what it should be. Also, this assumes your code is formatted in a fairly standard way. If it's not, it won't work.

Ryan Lundy
  • 204,559
  • 37
  • 180
  • 211
  • I'm sorry, the question was misleading, I meant namespaces used inside a class, not a file. I won't have any access to those files. – Conrad Clark Apr 28 '11 at 17:19
  • Do you need *namespaces*, or other referenced *assemblies*? The latter you can get with `Assembly.GetReferencedAssemblies()`; see http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getreferencedassemblies.aspx – Ryan Lundy Apr 28 '11 at 17:27
  • If you really mean namespaces, then I don't think you can do this. As far as I recall, in IL, names are fully-qualified. There are no `using` statements. – Ryan Lundy Apr 28 '11 at 17:29
  • `Assembly.GetReferencedAssemblies()` won't work for me. I guess I'll have to stick to manually setting up the environment based on the script. – Conrad Clark Apr 28 '11 at 17:31
1

You can use Mono Cecil to read a class definition from an assembly and get a list of all the referenced types in each method. From there, you can extract a list of namespaces (fully qualified type name minus the last part).

Mark Cidade
  • 98,437
  • 31
  • 224
  • 236