Determine if a property is an auto property - c#

I'm trying to figure out if a property is an auto property i.e.
public int Foo { get; set; }
Stared for a while at PropertyDeclarationSyntax and IPropertySymbol but did not find anything.
Guess an alternative is an extension method that evaluates if get & set does not contain any statements is one way but it does not feel very elegant.

Check whether any of the AccessorDeclarationSyntaxes in the PropertyDeclarationSyntax's AccessorList have a non-null Body.
You can see this by looking at any property declaration using the Syntax Visualizer (from the Roslyn SDK extension).

This blog gives a good explanation:
In summary,
var isExplicitProperty = node
.DescendantNodesAndSelf()
.OfType<PropertyDeclarationSyntax>()
.Any(prop =>
{
if(prop.AccessorList != null)
{
return prop.AccessorList.Accessors
.Any(a => a.Body != null || a.ExpressionBody != null);
}
// readonly arrow property declaration
return true;
});
Based on the internal source code

Related

How to check if a property is decorated with a custom attribute using Roslyn?

I want to analyse a C# class using Roslyn and intend to do something when visited property has the specific attribute applied to it. How can I do this in the CSharpSyntaxWalker.VisitPropertyDeclaration method override?
For example, in the following code block I want to know whether the Date property has the Validation attribute or not, and if so, whether IsJDate is true or false?
[Validation(IsJDate=true)]
public string Date {get; set;}
Initializations:
filesPath.ToList().ForEach(csFilePath =>
{
SyntaxTree csSyntaxTree = CSharpSyntaxTree.ParseText(csFileSourceCode);
// ....
}
_compiledCsCodes = CSharpCompilation.Create("CSClassesAssembly", csFiles.Select(cs => cs.CSSyntaxTree ), references);
foreach (CsFile csFile in csFiles)
{
csFile.FileSemanticModel = _compiledCsCodes.GetSemanticModel(csFile.FullSyntaxTree);
}
Finally, I found the solution by making some changes to Yuriy's answer as following:
foreach (var attribute in node.AttributeLists.SelectMany(al => al.Attributes))
{
if (csFile.FileSemanticModel.GetTypeInfo(attribute).Type.ToDisplayString() == "Proj.Attributes.ValidationAttribute")
{
var arg = attribute.ArgumentList.Arguments.FirstOrDefault(aa => aa.NameEquals.Name.Identifier.Text == "IsJDate");
if (arg != null && arg.Expression.IsKind(SyntaxKind.TrueLiteralExpression))
validationKind = ValidationKind.JDate;
}
}
Use the semantic model to get the bound ISymbol for the property, then call GetAttributes().

If Element does not exist

I have around a dozen solutions to this, but none seem to fit what I am trying to do. The XML file has elements that may not be in the file each time it is posted.
The trick is, the query is dependent upon a question value to get the answer value. Here is the code:
string otherphone = (
from e in contact.Descendants("DataElement")
where e.Element("QuestionName").Value == "other_phone"
select (string)e.Element("Answer").Value
).FirstOrDefault();
otherphone = (!String.IsNullOrEmpty(otherphone)) ? otherphone.Replace("'", "''") : null;
Under the "contact" collection, here are many elements named "DataElement", each with its own "QuestionName" and "Answer" elements, so I query to find the one where the element's QuestionName value is "other_phone", then I get the Answer value. Of course I will need to do this for each value I am seeking.
How can I code this to ignore the DataElement containing QuestionName with value of "other_phone" if it doesn't exist?
You can use Any method to check whether or not the elements exists :
if(contact.Descendants("DataElement")
.Any(e => (string)e.Element("QuestionName") == "other_phone"))
{
var otherPhone = (string)contact
.Descendants("DataElement")
.First(e => (string)e.Element("QuestionName") == "other_phone")
.Element("Answer");
}
Also, don't use Value property if you are using explicit cast.The point of explicit cast is avoid the possible exception if the element wasn't found.If you use both then before the cast, accessing the Value property will throw the exception.
Alternatively, you can also just use the FirstOrDefault method without Any, and perform a null-check:
var element = contact
.Descendants("DataElement")
.FirstOrDefault(e => (string)e.Element("QuestionName") == "other_phone");
if(element != null)
{
var otherPhone = (string)element.Element("Answer");
}
So you want to know if other_phone exists or not?
XElement otherPhone = contact.Descendants("QuestionName")
.FirstOrDefault(qn => ((string)qn) == "other_phone");
if (otherPhone == null)
{
// No question with "other_phone"
}
else
{
string answer = (string)otherPhone.Parent.Element("Answer");
}

Passing properties as parameters to be Got and Set

Well, I need to repeat same code for many properties.
I've seen examples taking Action delegates, but they don't fit quite well here.
I want something like this: (see explanation below)
Dictionary<Property, object> PropertyCorrectValues;
public bool CheckValue(Property P) { return P.Value == PropertyCorrectValues[P]; }
public void DoCorrection(Property P) { P.Value = PropertyCorrectValues[P]; }
.
I want to have a dictionary containing many properties and their respective "correct" values. (I know it's not well declared, but that's the idea). Properties are not necessarely inside my class, some of them are in objects of different assemblies.
A method bool CheckValue(Property). This method must access the actual value of the property and compare to the correct value.
And a method a void DoCorrection(Property). This one sets the property value to the correct value.
Remember I have many of those properties, I wouldn't like to call the methods by hand for each property. I'd rather iterate through the dicionary in a foreach statement.
So, the main question is in the title.
I've tried the by ref, but properties don't accept that.
Am I obligated to use reflection??? Or is there another option (if I need, reflection answer will be accepted as well).
Is there anyway I can make a dictionary with pointers in C#? Or some kind of assignment that changes the value of variable's target instead of changing the target to another value?
Thanks for the help.
You can do this using reflection. Get a list of the properties on the object of interest with typeof(Foo).GetProperties(). Your PropertyCorrectValues property can have type IDictionary<PropertyInfo, object>. Then use the GetValue and SetValue methods on PropertyInfo to perform the desired operations:
public bool CheckProperty(object myObjectToBeChecked, PropertyInfo p)
{
return p.GetValue(myObjectToBeChecked, null).Equals(PropertyCorrectValues[p]);
}
public void DoCorrection(object myObjectToBeCorrected, PropertyInfo p)
{
p.SetValue(myObjectToBeCorrected, PropertyCorrectValues[p]);
}
In addition to Ben's code I'd like to contribute the following code fragment:
Dictionary<string,object> PropertyCorrectValues = new Dictionary<string,object>();
PropertyCorrectValues["UserName"] = "Pete"; // propertyName
PropertyCorrectValues["SomeClass.AccountData"] = "XYZ"; // className.propertyName
public void CheckAndCorrectProperties(object obj) {
if (obj == null) { return; }
// find all properties for given object that need to be checked
var checkableProps = from props
in obj.GetType().GetProperties()
from corr in PropertyCorrectValues
where (corr.Key.Contains(".") == false && props.Name == corr.Key) // propertyName
|| (corr.Key.Contains(".") == true && corr.Key.StartsWith(props.DeclaringType.Name + ".") && corr.Key.EndsWith("." + props.Name)) // className.propertyName
select new { Property = props, Key = corr.Key };
foreach (var pInfo in checkableProps) {
object propValue = pInfo.Property.GetValue(obj, null);
object expectedValue = PropertyCorrectValues[pInfo.Key];
// checking for equal value
if (((propValue == null) && (expectedValue != null)) || (propValue.Equals(expectedValue) == false)) {
// setting value
pInfo.Property.SetValue(obj, expectedValue, null);
}
}
}
When using this "automatic" value correction you might also consider:
You cannot create a PropertyInfo object just by knowing the property name and independently of the declaring class; that's why I chose string for the key.
When using the same property name in different classes then you might need to change the code that is doing the actual assignment because the type between the correct value and the property type might differ.
Using the same property name in different classes will always perform the same check (see point above), so you might need a syntax for property names to restrict it to a specific class (simple dot notation, doesn't work for namespaces or inner classes, but might be extended to do so)
If needed you can replace the "check" and "assign" part with separate method calls, but it might be done inside the code block as stated in my example code.

c# copied property loses reference when referenced object is removed from list

Example
Have a look at the following code:
private void DeDuplicateOrganisations()
{
var profileOrgs = _organisations.Where(o => o.ExistsInProfile).ToList();
var kvkOrgs = _organisations.Where(o => !o.ExistsInProfile).ToList();
profileOrgs.ForEach(o =>
{
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
_organisations = profileOrgs.Concat(kvkOrgs).OrderBy(o => o.Title).ToList();
}
In this example the property CompanyInfoOrganisation (simply a get; set; property) is copied when an organisation is considered a duplicate. This all works as expected, duplicates are nicely deduplicated.
Also this is true inside this message:
_organisations.First(o => o.ExistsInBoth).CompanyInfoOrganisation != null;
Problem
Now I bind the _organisations list to a listbox
lbxCompanies.DataSource = null;
lbxCompanies.DataSource = _organisations;
lbxCompanies.DisplayMember = "Title";
lbxCompanies.SelectedIndex = -1;
and later on get the selected value:
var org = lbxCompanies.SelectedValue as Organisation;
gbxCompanyInfo.Visible = org != null;
if (gbxCompanyInfo.Visible)
if (org.CompanyInfoOrganisation != null)
// NEVER GETS HERE (but gbxComanpyInfo is visible)
If I try to read the CompanyInfoOrganisation property I always get null while I know the property was set.
Question
What is happening here? How come the property reference is destroyed? How can I prevent this from happening?
The reference you're using only has immediate scope and as soon as the query ends it exits scope and your reference disappears. So when you bind later, the reference is exactly right -- null.
profileOrgs.ForEach(o =>
{
// Right here -- var duplicate has scope ONLY within your query.
// As soon as the query is executed it leaves scope and the reference
// pointer will be null
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
Because you're using a class, you need to perform a deep MemberwiseClone on it to get a NEW copy of the object:
o.CompanyInfoOrganisation = (YourInfoType)duplicate.CompanyInfoOrganisation.MemberwiseClone();
When you load the data, load the CompanyInfoOrganisation property along with the root entity; that way it will be already loaded into memory. If using LINQ to SQL, you load via DataLoadOptions, and pass this to the context. If using Entity Framework, you use the Include method in the LINQ query.
It might have to do with capturing of variables inside the lambda. Try substituting the .ForEach to a regular foreach().
Or maybe the CompanyInfoOrganisation in duplicate was null to begin with.
The problem was I used string.Join() to show the values, and the first value to join was null (which is really annoying), resulting in an empty string, leaving me thinking the property was null. However it turned out the property was not null, but has a perfectly valid reference to the object needed. Using the debugger with a little more care would have saved me an hour or so...
Sorry!

C# Elegant way to handle checking for an item in a collection

I've posted a code sample below. Firstly let me explain
termStore.Groups in the code below is a collection of Group Objects (The exact class is irrelevant).
Checking for null : if (termStore.Groups[groupName] == null) seems like a logical (clean) approach, but if the Groups collection is empty then an exception is produced.
using the termStore.Groups.Contains is not an option either because this expects a strong type i.e: .Contains(Group)... not .Contains(GroupName as string)
Can someone recommend a clean / generic way I can check for if an item exists in collection .
Thank you....
TermStore termStore = session.TermStores.Where(ts => ts.Name == termStoreName).FirstOrDefault();
if (termStore.Groups[groupName] == null)
{
termStore.CreateGroup(groupName);
termStore.CommitAll();
}
Update: The exact class Sharepoint Taxonomy Classes. http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.taxonomy.group.aspx
Update 2, the exact collection : http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.taxonomy.groupcollection.aspx
Microsoft.SharePoint.Taxonomy.GroupCollection implements IEnumerable<Group>, so a bit of LINQ is probably just what the doctor ordered:-
if(termStore.Groups.Any(x => x.Name == "MyGroup"))
{
// group contains at least one item matching the predicate.
}
else
{
// group contains no items matching the predicate.
}
You'll need to be using .NET 3.5 or better and add "using System.Linq;" to the top of your file.
Edit
If you don't have LINQ available, or if it offends you, or if you've genuinely profiled and found that iterating over Groups is killing performance compared to the string indexer, you could use GroupCollection.Count to avoid the error state:-
if (termStore.Groups.Count == 0 || termStore.Groups[groupName] == null)
{
// Group doesn't exist.
}
IEnumerable.Any(...) should work for your case:
termsStore.Groups.Any()
I think this is what you are looking for:
termStore.Groups.ContainsKey(groupName)
Checks that the key exists, doesn't throw an exception if it doesn't. This is assuming that Groups is a collection that implements IDictionary.
May be this
termStore.Any() && termStore.Groups.Any() && termStore.Groups[groupName] == null
Ok, 2nd attempt. If Groups doesn't contain the required ContainsKey method then you can write it yourself. Then you can just use ContainsKey in place of Contains.
static class GroupExtensions
{
public static bool ContainsKey(this Groups groups, string key)
{
try
{
if(groups[key] == null)
{
return false;
}
}
catch
{
return false;
}
return true;
}
}

Categories

Resources