问题描述:

I want to parse a c# file. The only thing I want is to determine if it contains a property with a specific name; just a simple true/false response. Or rather, since I'd checking for more than one property in each run, extracting a list of property names could be helpful

I thought that I could create an elegant solution using the CodeDomProvider functionality (f# example):

use reader = new StreamReader(existingFile)

let codeProvider = new CSharpCodeProvider()

let codeUnit = codeProvider.Parse(reader)

Unfortunately, the Parse function is not implemented for the CSharpCodeProvider. Is there a way to get a CodeCompileUnit from a source file? Or is there another elegant way? (I had hoped to avoid regular expressions on this)?

Edit:

I'm going to use this for automatic code generation. Basically, I'm going to generate a partial class in file xyz.partial.cs. This will generate a skeleton property. But if I want to change the implementation of a property, I will cut that property and paste it into the hand coded xyz.cs. When recreating the generated class, I want it to skip generating properties that I have moved to the hand-coded file.

Therefore, reflection is out of the question, because reflection will tell me that the property does indeed exists, but not if it is defined in the one or the other file.

网友答案:

EDIT 2: Based on the added information, I'd say you're best of compiling the hand coded class and then reflecting that class. You can then generate the code for the partial class file.

EDIT: I've done some more research and it appears you are out of luck. CodeDom cannot be used to parse code. http://blogs.msdn.com/b/bclteam/archive/2005/03/16/396929.aspx

There is an example on how to create a CSharpCodeProvider instance on MSDN.

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CodeCompileUnit ccu = provider.Parse(reader);

You can then navigate the CodeCompileUnit (more documentation on CodeCompileUnit).

Hope it helps.

网友答案:

Found this, but have no experience -- good or bad -- with it: http://www.codeproject.com/KB/recipes/codedomparser.aspx

网友答案:

I think you can solve this by using PropertyInfo at runtime. It uses reflection to return all the info for a type. To get all the property names from a type, try this:

void GetMeTheProperties(object source)
{
    Type sourceType = source.GetType();

    foreach (PropertyInfo sourceProperty in sourceType.GetProperties())
    {
        int i = 1;
        Console.WriteLine("Property {0}: {1}", i, sourceProperty.Name;
    }
}

You can also determine if a specific named property is in a type by a similar method:

bool PropertyExists(string propertyName, object source)
{
    Type sourceType = source.GetType();
    return (from var s in sourceType.GetProperties() select s).Where(i => i.Name == propertyName).Any();
}
网友答案:

Is the file you are generating the code from compiled? If so, you could try creating an attribute to add to all the properties that shouldn't be copied. Then you can use reflection to read through the attributes and skip those ones.

internal class DoNotCopyAttribute: Attribute{}

// then add this to Odhran's GetMeTheProperties
bool skip=false;
foreach (System.Attribute attr in System.Attribute.GetCustomAttributes(sourceProperty)) {
  if (attr is DoNotCopyAttribute){ skip=true; break; }
}
if(skip) continue;
网友答案:

Sometimes RegEx is the only elegant solution. This should be what you're looking for. It will give you the names of every property in the code file, and nothing more:

(?:"(?:(?:(?:\\.)|[^"\\\r\n])*)"|'(?:(?:(?:\\.)|[^'\\\r\n])*)'|@"(?:(?:(?:"")|[^"])*)")|(?:(?://.*)|(?:/\*(?:(?:[^*]|\*(?!/))*)\*/))|(?:[\w?<>]\s+(\w+)\s*\{\s*(?:get|set)\s*[{;])

It will not match similar code in comments or strings and needs only a small modification to return the property's type. The name appears in capture \1, but will be blank if not a true match.

网友答案:
var provider = CodeDomProvider.CreateProvider("c#");
var parameters = new CompilerParameters
{
    WarningLevel = 3 // for example, tune how you need
};
var result = provider.CompileAssemblyFromSource(parameters, new string[] { "source" });

if (!result.Errors.HasErrors)
{
    var assembly = result.CompiledAssembly;

    bool containsLocalAppDomain = assembly
        .GetTypes()
        .SelectMany(t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
        .Any(p => p.Name == "YourProperty");

    // indeed it's much better not to load compiled assembly in current appDomain, but create a new one
    var appDomain = AppDomain.CreateDomain("YourNewDomain", null, null);
    bool containsNewAppDomain = appDomain
        .GetAssemblies()
        .SelectMany(a => a
             .GetTypes()
             .SelectMany(t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public)))
         .Any(p => p.Name == "YourProperty");

btw, how are you going to implement partial properties as far as the are not supported?

相关阅读:
Top