Check whether property is readonly - c#

I have this code using reflection and I decided to replace it by FastMember.
This is my code:
var VehicleType = TypeAccessor.Create(res.GetType());
var vehicleFastMember = ObjectAccessor.Create(res);
foreach (var kvp in dictionary)
{
if (kvp.Key == "Identifier") continue;
object value = kvp.Value;
if (VehicleType.GetMembers().FirstOrDefault(prop => prop.Name == kvp.Key) != null)
{
// here inside if i want to check whether property is not readonly,
// I am afraid of runtime exception that readonly cannot be overwritten.
**if (vehicleFastMember[kvp.Key].)**
{
vehicleFastMember[kvp.Key] = kvp.Value;
}
}
}
By reflection:
That row with stars would be solved by this line:
if (property?.CanWrite ?? false)
Does FastMember offer some elegant solution too?

According to the source code of fast-member's MemberSet.Member, CanRead/CanWrite are calling the same methods you are trying to call (PropertyInfo.Can[Read/Write]).
It's also worth noting that instead of determining if the property is writable, you could simply wrap the assignment into a try/catch, and catch the ReadOnlyException that would emerge and continue with the loop.

Related

Find string in a List, if match, string = List name

So my gameobject string name contain an ID(first variable) in the List, since the List is a custom class with two variable. I want to switch the name gameobject name to the second
variable after comparing the first variable and returning True.
if (gameData_List.showing_my_loading_list.Any(s => anchor.name.Contains(s.guid.ToString())))
{
Debug.Log("Found)
}
The result show 4 found, but what I'm trying to do is after knowing that is true, change the anchor.name to s.SecondVarible.Tostring();
However, after I set the condition in the if,
I no longer have access to the (s) in the Debug.Log area.
I managed to solve it myself, thanks
var temp = gameData_List.showing_my_loading_list.Find(x => anchor.name.Contains(x.guid.ToString()));
if (temp != null)
{
anchor.name = temp.readable_guid;
Debug.Log("Changing Name");
}
If you do not want to split it into lines you could probably do something like
anchor.name = gameData_List.showing_my_loading_list.FirstOrDefault(s => anchor.name.Contains(s.guid.ToString()))?.readable_guid ?? anchor.name;
So this does
FirstOrDefault: Try to find an element s where the condition is matched or return default (= null for classes)
?.: If it is not null use the readable_guid, otherwise return null again
??: If the before is null use anchor.name (the old one) as fallback => change nothing
Of course this loses the ability to do more things when you found a valid element like your logging etc.
Alternatively, why not simply stick to a normal loop ;)
foreach(var s in gameData_List.showing_my_loading_list)
{
if(anchor.name.Contains(s.guid.ToString())
{
anchor.name = s.readable_guid;
Debug.Log("Changing Name");
break;
}
}
or with Linq again
foreach (var s in gameData_List.showing_my_loading_list.Where(s => anchor.name.Contains(s.guid.ToString())))
{
anchor.name = s.readable_guid;
Debug.Log("Changing Name");
break;
}
gameData_List.showing_my_loading_list.filter(s => anchor.name.Contains(s.guid.ToString())).each(() => {
Debug.Log("Found)
})

How to safely check if a dynamic object has a field or not

I'm looping through a property on a dynamic object looking for a field, except I can't figure out how to safely evaluate if it exists or not without throwing an exception.
foreach (dynamic item in routes_list["mychoices"])
{
// these fields may or may not exist
int strProductId = item["selectedProductId"];
string strProductId = item["selectedProductCode"];
}
using reflection is better than try-catch, so this is the function i use :
public static bool doesPropertyExist(dynamic obj, string property)
{
return ((Type)obj.GetType()).GetProperties().Where(p => p.Name.Equals(property)).Any();
}
then..
if (doesPropertyExist(myDynamicObject, "myProperty")){
// ...
}
This is gonna be simple. Set a condition which checks the value is null or empty. If the value is present, then assign the value to the respective datatype.
foreach (dynamic item in routes_list["mychoices"])
{
// these fields may or may not exist
if (item["selectedProductId"] != "")
{
int strProductId = item["selectedProductId"];
}
if (item["selectedProductCode"] != null && item["selectedProductCode"] != "")
{
string strProductId = item["selectedProductCode"];
}
}
You need to surround your dynamic variable with a try catch, nothing else is the better way in makking it safe.
try
{
dynamic testData = ReturnDynamic();
var name = testData.Name;
// do more stuff
}
catch (RuntimeBinderException)
{
// MyProperty doesn't exist
}

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.

possible null assignment in Dictionary Contains Key

Getting a warning on "!redirectsDictionary.ContainsKey(autoRedirect.Key)"
asp.net possible null assignment to entity marked with "notnull" attribute.
Just wondering what that's about ?
private static readonly Dictionary<string, Redirect> AutoRedirectsDictionary = new Dictionary<string, Redirect>();
foreach (var r in db.SelectItems("fast:/sitecore/content/Redirects Root//*[##templatename='Auto Redirect']"))
{
GenerateRedirects(Context.Database.GetItem(r.Fields["Root Node"].Value), r["URL Prefix"]);
foreach (var autoRedirect in AutoRedirectsDictionary)
{
if (!string.IsNullOrEmpty(autoRedirect.Key) & !redirectsDictionary.ContainsKey(autoRedirect.Key))
{
//Add to dictionary
redirectsDictionary.Add(autoRedirect.Key, autoRedirect.Value);
}
}
}
public static void GenerateRedirects(Item redirectFolder, string urlPrefix)
{
if (redirectFolder == null)
return;
var childList = redirectFolder.GetChildren();
foreach (Item child in childList)
{
if (Utilities.HasFieldValue(child, FieldToFind))
{
var shortcutUrl = urlPrefix + child.Fields[FieldToFind].Value.ToLower();
if (!string.IsNullOrEmpty(shortcutUrl) && !AutoRedirectsDictionary.ContainsKey(shortcutUrl))
{
AutoRedirectsDictionary.Add(shortcutUrl,
new Redirect(String.Empty, child, true));
}
}
else
{
GenerateRedirects(child, urlPrefix);
}
}
}
It might have to do with your use of the single & operator. A single & will not use short-circuiting to bypass a statement but will instead choose the path to execute after all expressions have been evaluated. So even though you're checking !string.IsNullOrEmpty(autoRedirect.Key) prior to the ContainsKey call, both expressions will be evaluated first and then the path of execution will be decided.
Edited as I realized I didn't truly answer your specific question (and you may already know this) but !redirectsDictionary.ContainsKey(autoRedirect.Key) will throw an exception if the key is null. Since the datatype for the key is a string there is a possibility it will throw an exception if it is null, hence the warning.

LINQ Abbreviation Tip

I've a LINQ query like below:
foreach (var property in from property in properties where property.Name != "Type" select property)
{
}
how would you go about making this statement more concise without using the actual extension method which looks unattractive (i.e. without using .Where like: foreach (var property in properties.Where(...)).
You cant really..
You could put the query into a separate line.
var selectedProperties = from property in properties
where property.Name != "Type"
select property;
foreach (var property in selectedProperties)
{
}
Or you could factor the query out into a separate method if it is really huge.
foreach ( var property in ComplexSelectionOfProperties () )
...
But really I would say the exention method in this case is much neater. Its only when the queries get more complex and involve joins that the query syntax becomes tidier. (IMHO)
Beauty is always in the eye of the beholder :)
However in such a case I would go create a method that filters non-Type properties and iterate over its results.
Something like this:
IEnumerable<IProperty> GetNonTypeProperties(IEnumerable<IProperty> properties)
{
return (from property in properties where property.Name != "Type" select property);
}
void foo()
{
foreach (var property in GetNonTypeProperties(properties))
{
}
}
The lack of conciseness comes precisely from the sql style syntax : using a "dot" notation you will sensibly shorten your expression :
foreach (var property in properties.Where(property => property.Name != "Type"))
{
}
If you want to shorten the longest part which is obviously the boolean test, you have to put it elsewhere.
Either in the foreach loop itself :
foreach (var property in properties)
{
if(property.Name != "Type")
{
...
}
}
Either if a separate function :
foreach (var property in properties.Where(IsNotType))
{
}
//and farther :
bool IsNotType(Property p)
{
return property.Name != "Type";
}
But anyway you want to perform a loop with a test on each element, so in a way or another you will have to code that and it will take a minimum amount of characters.
Just in case that you don't like the lambda expression, not the extension method itself, you can make your own extension method with query inside, like this:
public static IEnumerable<Property> PropertiesExceptType(this IEnumerable<Property> properties) {
return from property in properties
where property.Name != "Type"
select property;
}
and use it:
foreach(var property in properties.PropertiesExceptType()) {
// ...
}
The good thing about encapsulating your query in separate method is that you can debug the method with loop and change the code on the fly (VS won't let you do this if you have a linq query right inside this method).
I honestly don't see anything wrong with var propery in properties.Where(), it's much better than any query expression in this context IMO. But if you want to stick with your query, at least introduce a variable:
var filteredProperties = from property in properties
where property.Name != "Type"
select property;
foreach(var property in filteredProperties)
{
// ...
}
People who will read and debug it later will thank you. But I still think that extension method is the way to go here

Categories

Resources