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 th开发者_C百科ought 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.
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?
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.
精彩评论