With the DLR, i would like to do something like this:
class MyClass {
int MyProperty { get; set; }
}
In razor, I would do something like this. (InstanceOfMyClass is some dynamic object that looks at an instance of MyClass)
#InstanceOfMyClass.MyProperty
This would output the string representation of MyProperty.
Now if I do this.
#InstanceOfMyClass.MyMissingProperty
I would like it to output "Missing: MyMissingProperty". I would love to capture the whole expression, like so.
#InstanceOfMyClass.MyMissingProperty.MoreMissing
Could potentially output "Missing: MyMissingProperty.MoreMissing", but that might be asking a lot of the DLR.
Will the ExpandoObject allow me to do this? If not, what do I have to do to implement this?
Extend DynamicObject.TryGetMember in this way:
If the member exists, return the value. If the member doesn't exist, return a new instance of a class that will handle both the string representation of the missing property and also the chain. Something like this
public class MissingPropertyChain : DynamicObject
{
private string property;
public MissingPropertyChain(string property)
{
this.property = property;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if(binder.Name == "ToString")
result = "Missing property: " + property;
else
result = new MissingPropertyChain( property + "." + binder.Name;
return true;
}
}
I didn't try it, but I think it will give you the idea of how to solve the problem.
Hope it helps.
I am not sure about the Expando, it's rather used when you want to set the property and then get it. From what you write, however, it seems that you'd like to be able to read any value which hasn't been set before.
For this, the DynamicObject could be used. You just override the TryGetMember.
You could achieve that by creating your own version of DynamicObject then over-write the TryGetMember
http://haacked.com/archive/2009/08/26/method-missing-csharp-4.aspx
Related
I have a solution where i get to map out all properties and sub-properties of an object into a Dictionary.
Lets say I have something like these objects:
class MyClassA{
string info;
}
class MyClassB{
string info;
}
class MyClassC{
MyClassA a;
MyClassB b;
string something;
}
class MyClassD{
MyClassC c;
}
I created an utility to map out all the propreties so i can get something like:
MyClassD dObject = Something();
Dictionary<string, PropertyInfo> propertyMap = new Dictionary<string, PropertyInfo>();
propertyMap = buildPropertyMap(dObject );
where the string is the path and the PropertyInfo the actual property. The strings on the Map on this example would look like this (pseudo-output):
propertyMap.Keys={
c;
c.a;
c.b;
c.a.info;
c.b.info;
c.something;
}
This is a great way to tell what goes where when reading for example data from an excel file and not from xml-like things, like so:
ExcelTableC:
-----------------------
1|Ainfo|Binfo|Csomething|
-------------------------
2|value|value|valuevalue|
3|value|value|valuevalue|
-----------------------
It wall works great.
Now thing is, this is all obviously inside a couple of loops and diferent functions (because of the Excel reading process) and i need later to get the key, this is, lets say I have this 'property' and I want the path (dont ask why):
// this method does not exist (pseudo-code)
PropertyInfo.GetPath; //it would return 'c.b.info' for ex. or 'c.a.info'
So i wanted to implement a class that extended PropertyInfo to add my methods.
But doing something like:
public class PropertyField : PropertyInfo
{
PropertyField parent;
string path;
// etc...
}
returns error because PropertyInfo is an abstract class and I would need to implement all inhereted members.
I can add 'abstract' to 'PropertyField' like this:
public abstract class PropertyField : PropertyInfo {}
But then when i try to cast it like this :
private void findProperties(Type objType)
{
PropertyInfo[] properties = objType.GetProperties();
for (int i=0; i< properties.Length; i++)
{
//PropertyInfo propertyInfo = properties[i];
PropertyField property = (PropertyField) properties[i];
//do something with it
}
}
will return the following error:
System.InvalidCastException: Unable to cast object of type
'System.Reflection.RuntimePropertyInfo' to type 'App.models.PropertyField'.
So the question is, how do I add these methods? If I cant inherit what can i do?
You should do:
public class PropertyField
{
PropertyField parent;
string path;
PropertyInfo info;
// etc...
}
I see no point in inheriting PropertyInfo.
Use exstension method to extend the PropertyInfo class instead of inhriting it.
public static class Extensions
{
public static string GetPath(this PropertyInfo pi)
{
// Your implementation to get the path
}
}
The short answer is, better derive from PropertyDescriptor than from PropertyInfo. While the latter is used by the runtime environment and instantiated in RuntimePropertyInfo objects, the latter is used really to meet your purpose, to describe the property. Property descriptors have been used quite a lot in Windows.Forms, basically the whole WinForms designer is built on them and they are quite powerful.
And: you can easily inherit ``PropertyDescriptorand you even do not have to recreate a whole lot of functionality sincePropertyDescriptor` has constructors that allow you to pass in a name and a set of attributes.
Is there a way to do something like this in C#:
public class Class2 {
public string PropertyName1 { get
{
return this; //i mean "PropertyName1"
}
set {
this = value;
DoAdditionalFunction();
}
}
Because I need to call additional function in the "set" I need to have an extra private field like
private string _propertyName1;
public string PropertyName1 { get
{
return _propertyName1;
}
set {
_propertyName1= value;
DoAdditionalFunction();
}
I don't want to use additional property like _propertyName1. Is there a way to accomplish this or any best practices?
No - if you need any behaviour other than the most trivial "set a field, return the field value", you need to write "full" properties. Automatically implemented properties are only a shorthand for trivial properties.
Note that you haven't really got an "extra" private field, in terms of the actual contents of an object - it's just that you're explicitly declaring the private field instead of letting the compiler do it for you as part of the automatically implemented property.
(It's not clear what your first property is trying to do - setting this in a class is invalid, and you can't return this from a property of type string unless you've got a conversion to string...)
This is not a duplicate of this question. I have to serialize the Property which is "ReadOnly". I can't do anything on this class, because this is System.Web.Security.MembershipUser class, of course this is not sealed class.
[WebGet]
public string GetAllUsers()
{
List<MembershipUser> membershipList = new List<MembershipUser>();
MembershipUserCollection userCollection = Membership.GetAllUsers();
foreach (MembershipUser user in userCollection)
membershipList.Add(user);
string memberCollection = SerializeToString(membershipList, typeof(List<MembershipUser>));
List<MembershipUser> users = Deserialize(memberCollection, typeof(List<MembershipUser>)) as List<MembershipUser>;
return memberCollection;
}
Above code is what I used,
MembershipUserCollection userCollection = Membership.GetAllUsers();
GetAllUsers method returns MembershipUserCollection, but this does not have default accessor. So while serializing I get exception. That is the reason I went with List<MembershipUser>. Here too I face trouble. This is eating my day, what could solve this?.
Edit:
I'm using XmlSerializer.
Serializing will only serialize public fields as well as public properties that you can both get and set. The reason for the latter is that if you cannot set it, then when you go to deserialize it, how do you set the property?
Since the class isn't sealed, you could inherit from it, define a setter, but have it do nothing, i.e.
public string Name
{
get {return _name;}
set { }
}
The thing to look out for is when you deserialize to that class, the data will be lost.
HTH,
Brian
I would suggest creating a wrapper over MembershipUserCollection for your serialization/deserialization purpose.
Also, Are you sure that above code is throwing exception because fields have private members? It may be because of missing Serializableattribute or default constructor!!!
Taking a look at the following question, Real world use of custom .NET attributes how would you implement the solution proposed by #Esteban?
I'm a little confused as to when and where the code would get executed I think. Could you please supply a good sample of code.
I've asked this question before but didn't properly phrase it I think so...
With respect to the question/answer that you reference, I assume that there would be some code that runs either in the data layer or in the class itself that does validation. That code would use Reflection on the object being validated to find the properties with different attributes and run the specific validation logic associated with that attribute on that property.
It might look something like the following:
public void Validate( object obj )
{
foreach (var property in obj.GetType().GetProperties())
{
var attribute = property.GetCustomAttributes(typeof(ValidationAttribute), false);
var validator = ValidationFactory.GetValidator( attribute );
validator.Validate( property.GetValue( obj, null ) );
}
}
On submit(save) of the html form(win form) you get back changed Customer class. For each property you check if it has custom attribute(inherited from ValidationAttribute or implementing IValiador interface or something like this) associated with it. For each such property you call the validate method of attribute(create appropriate validation class and call validate method) on the property value.
You would use reflection:
public class MyClass
{
[Description("I'm an attribute!")]
public int MyField;
public Attribute GetAttribute(string fieldName)
{
FieldInfo field = typeof(MyClass).GetField("MyField");
Attribute[] attributes = (Attribute[])field.GetCustomAttributes(typeof(Attribute), false);
DescriptionAttribute desc = (DescriptionAttribute)attributes[0];
return desc;
}
}
If the attributed member is a field, you would use FieldInfo, as used in the example. If it's a property, you would use PropertyInfo, the members of FieldInfo and PropertyInfo are pretty much the same.
I'm sending xml to another program, which expects boolean flags as "yes" or "no", rather than "true" or "false".
I have a class defined like:
[XmlRoot()]
public class Foo {
public bool Bar { get; set; }
}
When I serialize it, my output looks like this:
<Foo><Bar>true</Bar></Foo>
But I would like it to be this:
<Foo><Bar>yes</Bar></Foo>
Can I do this at the time of serialization? I would prefer not to have to resort to this:
[XmlRoot()]
public class Foo {
[XmlIgnore()]
public bool Bar { get; set; }
[XmlElement("Bar")]
public string BarXml { get { return (Bar) ? "yes" : "no"; } }
}
Note that I also want to be able to deserialize this data back again.
Ok, I've been looking into this some more. Here's what I've come up with:
// use this instead of a bool, and it will serialize to "yes" or "no"
// minimal example, not very robust
public struct YesNo : IXmlSerializable {
// we're just wrapping a bool
private bool Value;
// allow implicit casts to/from bool
public static implicit operator bool(YesNo yn) {
return yn.Value;
}
public static implicit operator YesNo(bool b) {
return new YesNo() {Value = b};
}
// implement IXmlSerializable
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader) {
Value = (reader.ReadElementContentAsString() == "yes");
}
public void WriteXml(XmlWriter writer) {
writer.WriteString((Value) ? "yes" : "no");
}
}
Then I change my Foo class to this:
[XmlRoot()]
public class Foo {
public YesNo Bar { get; set; }
}
Note that because YesNo is implicitly castable to bool (and vice versa), you can still do this:
Foo foo = new Foo() { Bar = true; };
if ( foo.Bar ) {
// ... etc
In other words, you can treat it like a bool.
And w00t! It serializes to this:
<Foo><Bar>yes</Bar></Foo>
It also deserializes correctly.
There is probably some way to get my XmlSerializer to automatically cast any bools it encounters to YesNos as it goes - but I haven't found it yet. Anyone?
Very simple. Use a surrogate property. Apply XmlIgnore on the actual property. The surrogate is a string, and must use the XmlElement attribute that takes a element-name override. Specify the name of the actual property in the override. The surrogate property serializes differently based on the value of the actual property. You must also provide a setter for the Surrogate, and the setter should set the actual property appropriately, for whatever value it serialized. In other words it needs to go both ways.
Snip:
public class SomeType
{
[XmlElement]
public int IntValue;
[XmlIgnore]
public bool Value;
[XmlElement("Value")]
public string Value_Surrogate {
get { return (Value)? "Yes, definitely!":"Absolutely NOT!"; }
set { Value= (value=="Yes, definitely!"); }
}
}
click here for full compilable source example.
Making a bool value serialize as "yes" or "no" changes the data type from being a boolean at all. Instead, can you add a separate property which evaluates a boolean and returns "yes" or "no" as appropriate for it's data type? Maybe you could even force "yes" or "no" by making the return type be an enum which only specifies those values.
public YesOrNo DoYouLoveIt
{
get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; }
}
That might be overkill, but might answer your need. The only reason I bring up an enum for such a simple value is you'd be restricting the values vs. allowing any string.
I use the property method, but instead of checking to see if the string is equal to yes or no, I prefer to check if the string starts with (case insensitive) "YT1". This allows the file to contain true, True, t, T, y, Y, yes, Yes, 1, etc. all which will evaluate to true. While I can specify that false is false, False, f, F, n, N, no, No, 0, etc., anything that doesn't match the true still evaluates to false.
Your property example is probably the simplest way you could do it. If it helps, I believe you don't need to make it a public property, since the attribute implements ISerializable on the class behind your back. To enable deserialization, you should be able to just implement set { Bar = value == "yes"; }
#Blorgbeard:
If you have more then one of these YesNo classes in an object class,
make sure to read the entire element.
public void ReadXml(XmlReader reader)
{
string element = reader.ReadOuterXml();
int startIndex = element.IndexOf('>') + 1;
int length = element.LastIndexOf('<') - startIndex;
string text = (element.Substring(startIndex, length).ToLowerInvariant();
Value = (text == "yes");
}
Otherwise this might cause problems.
The ReadXml method must reconstitute your object using the information that was written by the WriteXml method.
When this method is called, the reader is positioned at the start of the element that wraps the information for your type. That is, just
before the start tag that indicates the beginning of a serialized
object. When this method returns, it must have read the entire element
from beginning to end, including all of its contents. Unlike the
WriteXml method, the framework does not handle the wrapper element
automatically. Your implementation must do so. Failing to observe
these positioning rules may cause code to generate unexpected runtime
exceptions or corrupt data.
What you're needing to do sounds more like a display issue. If your application allows, you will be better off keeping the data type as a boolean and displaying Yes/No in your user interface.