I have a class that creates a static array of all properties, using a static constructor. I also have a function -- GetNamesAndTypes() -- that lists the name & type of each property in that array.
Now I want to create another instance-level function -- GetNamesAndTypesAndValues() -- that displays the name & type of each property in the class, as well as that instance's value. How would I do that? Here's the code that I've written so far:
//StaticTest.cs
using System;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
namespace StaticTest
{
public class ClassTest
{
private string m_A, m_B, m_C;
private static PropertyInfo[] allClassProperties;
static ClassTest()
{
Type type = typeof(ClassTest);
allClassProperties = type.GetProperties();
// Sort properties alphabetically by name
// (http://www.csharp-examples.net/reflection-property-names/)
Array.Sort(allClassProperties, delegate(PropertyInfo p1, PropertyInfo p2)
{
return p1.Name.CompareTo(p2.Name);
});
}
public int A
{
get { return Convert.ToInt32(m_A); }
set { m_A = value.ToString(); }
}
public string B
{
get { return m_B; }
set { m_B = value; }
}
public DateTime C
{
get { return DateTime.ParseExact("yyyyMMdd", m_C,
CultureInfo.InvariantCulture); }
set { m_C = String.Format("{0:yyyyMMdd}", value); }
}
public static void GetNamesAndTypes()
{
foreach (PropertyInfo propertyInfo in allClassProperties)
{
Console.WriteLine("{0} [type = {1}]", propertyInfo.Name,
propertyInfo.PropertyType);
}
}
public void GetNamesAndTypesAndValues()
{
foreach (PropertyInfo propertyInfo in allClassProperties)
{
Console.WriteLine("{0} [type = {1}]", propertyInfo.Name,
propertyInfo.PropertyType);
}
}
}
}
//Program.cs
using System;
using System.Collections.Generic;
using StaticTest;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("[static] GetNamesAndTypes()");
ClassTest.GetNamesAndTypes();
Console.WriteLine("");
ClassTest classTest = new ClassTest();
classTest.A = 4;
classTest.B = #"bacon";
classTest.C = DateTime.Now;
Console.WriteLine("[instance] GetNamesAndTypesAndValues()");
classTest.GetNamesAndTypesAndValues();
Console.ReadLine();
}
}
}
I tried using propertyInfo.GetValue(), but I couldn't get it to work.
In your example propertyInfo.GetValue(this, null) should work. Consider altering GetNamesAndTypesAndValues() as follows:
public void GetNamesAndTypesAndValues()
{
foreach (PropertyInfo propertyInfo in allClassProperties)
{
Console.WriteLine("{0} [type = {1}] [value = {2}]",
propertyInfo.Name,
propertyInfo.PropertyType,
propertyInfo.GetValue(this, null));
}
}
Related
using System;
namespace zestaw_6
{
public class Program
{
public static void Main(string[] args)
{
var zoo = new Zoo();
zoo.Add(new Opiekun("Jan", "Kowalski"));
Console.ReadKey();
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace zestaw_6
{
public static class ActionExtensions
{
public static IList<TObj> Set<TObj, TAg>(this TAg aggregatedObj) where TObj : IInfo where TAg : IAction
{
var aggregatedObjType = aggregatedObj.GetType();
var propertyInfo = aggregatedObjType.GetProperties().FirstOrDefault(p => p.PropertyType == typeof(IList<TObj>));
var propertyValue = propertyInfo?.GetValue(aggregatedObj);
return propertyValue as IList<TObj>;
}
public static C Get<C>(this IAction container, Func<C, bool> searchPredicate = null) where C : IInfo
{
return searchPredicate == null ? container.Set<C, IAction>().FirstOrDefault() : container.Set<C, IAction>().FirstOrDefault(searchPredicate);
}
public static IList<C> GetList<C>(this IAction container, Func<C, bool> searchPredicate = null) where C : IInfo
{
return searchPredicate == null ? container.Set<C, IAction>() : container.Set<C, IAction>().Where(searchPredicate).ToList();
}
public static S Add<C, S>(this S container, C element) where S : IAction where C : IInfo
{
container.Set<C, IAction>().Add(element);
return container;
}
public static C Remove<C>(this IAction container, Func<C, bool> searchFn) where C : IInfo
{
var obj = container.Set<C, IAction>().SingleOrDefault(searchFn);
if(obj != null)
{
container.Set<C, IAction>().Remove(obj);
}
return obj;
}
public static C AddInto<C>(this C obj, IAction container) where C : IInfo
{
container.Set<C, IAction>().Add(obj);
return obj;
}
public static void ForEach<T>(this IList<T> list, Action<T> action) where T : IInfo
{
for(int i = 0; i < list.Count; i++)
{
action(list[i]);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace zestaw_6
{
public class Zoo : IInfo, IAction
{
public List<Klatka> klatki = new List<Klatka>();
public List<Opiekun> opiekunowie = new List<Opiekun>();
public void DisplayInfo()
{
foreach (var item in klatki)
{
item.DisplayInfo();
}
foreach (var item in opiekunowie)
{
item.DisplayInfo();
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace zestaw_6
{
public class Opiekun : IInfo, IAction
{
public List<Klatka> klatki = new List<Klatka>();
public string imie { get; set; }
public string nazwisko { get; set; }
public Opiekun(string imie_, string nazwisko_)
{
imie = imie_;
nazwisko = nazwisko_;
}
public void DisplayInfo()
{
Console.WriteLine("Imie: {0}", imie);
Console.WriteLine("Nazwisko: {0}", nazwisko);
foreach (var item in klatki)
{
item.DisplayInfo();
}
}
}
}
Basically, program gives expection "System.NullReferenceException: „Object reference not set to an instance of an object.” at Add function container.Set<C, IAction>().Add(element);. container.Set<C, IAction>() returns null and i dont know why. Funcion Add in main should Add new object to list "opiekunowie" in class zoo. What is the reason for that to not work?
Your code uses reflection, but doesn't find what it wants, for two reasons.
The reflection is done here:
public static IList<TObj> Set<TObj, TAg>(this TAg aggregatedObj) where TObj : IInfo where TAg : IAction
{
var aggregatedObjType = aggregatedObj.GetType();
var propertyInfo = aggregatedObjType.GetProperties().FirstOrDefault(p => p.PropertyType == typeof(IList<TObj>));
var propertyValue = propertyInfo?.GetValue(aggregatedObj);
return propertyValue as IList<TObj>;
}
It looks for properties of type IList<something> (this is reason 1).
You inside Zoo have:
public List<Klatka> klatki = new List<Klatka>();
public List<Opiekun> opiekunowie = new List<Opiekun>();
fields of type List<something>
So correct like this (to have properties):
public List<Klatka> klatki { get; set; } = new List<Klatka>();
public List<Opiekun> opiekunowie { get; set; } = new List<Opiekun>();
and then (to check for properties of a type assignable to IList<something> (this is reason 2)
var propertyInfo = aggregatedObjType.GetProperties().FirstOrDefault(p => typeof(IList<TObj>).IsAssignableFrom(p.PropertyType));
ASP.NET MVC4 application uses modified WebMatrix Dynamicrecord to get dynamic data from ADO.NET and show it in a WebGrid.
Running application causes strange exception
Invalid attempt to read when no data is present
in method
private object GetNonNullValue(int i)
at line
var value = Record[i];
Using foreach as shown in comment does not work in Mono as discussed in https://github.com/npgsql/npgsql/issues/295 . So application uses while but while does not work in Windows also.
How to get dynamic data in while loop ?
Whole solution is available at http://wikisend.com/download/360760/invalidattemptoreadwhennodataispresent.zip
Controller:
using Eeva.Business;
using Eeva.Erp.ViewModels;
using Npgsql;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Web.Mvc;
namespace Eeva.Erp.Controllers
{
public class ReportController : Controller
{
public ActionResult Test()
{
var data = TestData();
return View("ReportData", new ReportDataViewModel(data, ""));
}
IEnumerable<dynamic> TestData()
{
using (var connection = new NpgsqlConnection(ConnectionString()))
{
connection.Open();
DbCommand command = (DbCommand)connection.CreateCommand();
command.CommandText = "select 'A' union select 'B'";
using (command)
{
using (DbDataReader reader = command.ExecuteReader())
{
IEnumerable<string> columnNames = null;
while (reader.Read())
{
if (columnNames == null)
columnNames = GetColumnNames(reader);
yield return new EevaDynamicRecord(columnNames, reader);
}
//foreach (DbDataRecord record in reader)
//{
// if (columnNames == null)
// columnNames = GetColumnNames(record);
// yield return new EevaDynamicRecord(columnNames, record);
//}
}
}
}
}
static IEnumerable<string> GetColumnNames(IDataRecord record)
{
// Get all of the column names for this query
for (int i = 0; i < record.FieldCount; i++)
yield return record.GetName(i);
}
static string ConnectionString()
{
return new NpgsqlConnectionStringBuilder()
{
Host = "localhost",
UserName = "postgres",
}.ConnectionString;
}
}
}
ViewModel:
using System.Collections.Generic;
using System.Web.Mvc;
using Eeva.Business;
namespace Eeva.Erp.ViewModels
{
public class ReportDataViewModel
{
public IEnumerable<dynamic> Rows { get; set; }
public string Source;
public ReportDataViewModel(IEnumerable<dynamic> rows, string source)
{
Rows = rows;
Source = source;
}
}
}
View:
#model Eeva.Erp.ViewModels.ReportDataViewModel
#using System.Web.Helpers
#{ Layout = null;
var gd = new WebGrid(source: Model.Rows );
}
<!DOCTYPE html>
<html>
<head></head>
<body>
#gd.GetHtml()
</body>
</html>
Dynamicrecord is used from MVC4 source code with modifications:
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Text;
using WebMatrix.Data.Resources;
namespace Eeva.Business
{
public sealed class EevaDynamicRecord : DynamicObject, ICustomTypeDescriptor
{
public EevaDynamicRecord(IEnumerable<string> columnNames, IDataRecord record)
{
Debug.Assert(record != null, "record should not be null");
Debug.Assert(columnNames != null, "columnNames should not be null");
Columns = columnNames.ToList();
Record = record;
}
public IList<string> Columns { get; private set; }
private IDataRecord Record { get; set; }
public object this[string name]
{
get
{
for (int i = 0; i < Record.FieldCount; i++)
{
string normname = Record.GetName(i);
if (normname.Equals(name, StringComparison.InvariantCultureIgnoreCase))
return GetNonNullValue(i);
}
throw new InvalidOperationException("No column " + name);
}
}
public object this[int index]
{
get
{
return GetNonNullValue(index); // GetValue(Record[index]);
}
}
public string Field(int fldNo)
{
return Record.GetName(fldNo).ToUpperInvariant();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this[binder.Name];
return true;
}
private object GetNonNullValue(int i)
{
var value = Record[i];
if (DBNull.Value == value || value == null)
{
var tt = Record.GetFieldType(i).Name;
switch (tt)
{
case "Decimal":
case "Int32":
case "Double":
return 0;
case "String":
return "";
case "DateTime":
return null;
case "Boolean":
// kui seda pole, siis demos lao kartoteek kartoteegi kaart annab vea:
return false;
}
return null;
}
if (value is decimal? || value is decimal)
return Convert.ChangeType(value, typeof(double));
if (value is string)
return value.ToString().TrimEnd();
return value;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return Columns;
}
private void VerifyColumn(string name)
{
// REVIEW: Perf
if (!Columns.Contains(name, StringComparer.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
"Invalid Column Name " + name));
}
}
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return AttributeCollection.Empty;
}
string ICustomTypeDescriptor.GetClassName()
{
return null;
}
string ICustomTypeDescriptor.GetComponentName()
{
return null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return null;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return null;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return null;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return EventDescriptorCollection.Empty;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return EventDescriptorCollection.Empty;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
return ((ICustomTypeDescriptor)this).GetProperties();
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
// Get the name and type for each column name
var properties = from columnName in Columns
let columnIndex = Record.GetOrdinal(columnName)
let type = Record.GetFieldType(columnIndex)
select new DynamicPropertyDescriptor(columnName, type);
return new PropertyDescriptorCollection(properties.ToArray(), readOnly: true);
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
private class DynamicPropertyDescriptor : PropertyDescriptor
{
private static readonly Attribute[] _empty = new Attribute[0];
private readonly Type _type;
public DynamicPropertyDescriptor(string name, Type type)
: base(name, _empty)
{
_type = type;
}
public override Type ComponentType
{
get { return typeof(EevaDynamicRecord); }
}
public override bool IsReadOnly
{
get { return true; }
}
public override Type PropertyType
{
get { return _type; }
}
public override bool CanResetValue(object component)
{
return false;
}
public override object GetValue(object component)
{
EevaDynamicRecord record = component as EevaDynamicRecord;
// REVIEW: Should we throw if the wrong object was passed in?
if (record != null)
{
return record[Name];
}
return null;
}
public override void ResetValue(object component)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
"DataResources.RecordIsReadOnly", Name));
}
public override void SetValue(object component, object value)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
"DataResources.RecordIsReadOnly", Name));
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}
}
I posted this also in http://forums.asp.net/p/2013821/5795169.aspx
Update
Problably creating a copy of every dynamicrecord solves the issue:
Dictionary<string, object> Clonedict;
public EevaDynamicRecord Clone()
{
var res = new EevaDynamicRecord(Columns, Record);
res.Clonedict = new Dictionary<string, object>();
for (int i = 0; i < Record.FieldCount; i++)
res.Clonedict[Record.GetName(i)] = Record[i]);
return res;
// this also does not work:
// return (EevaDynamicRecord)this.MemberwiseClone();
}
Is there better solution ?
I do not know that this will necessarily work, but to suggest it I needed to post an answer since it includes a code sample.
If I'm right and the problem is occurring because you're attempting to access data from a reader after the command has gone out of scope, this will work around that.
Replace your Test method with this:
public ActionResult Test()
{
var data = TestData().ToArray();
return View("ReportData", new ReportDataViewModel(data, ""));
}
If that doesn't help, try replacing the TestData method with this:
IEnumerable<dynamic> TestData()
{
List<dynamic> results = new List<dynamic>();
using (var connection = new NpgsqlConnection(ConnectionString()))
{
connection.Open();
DbCommand command = (DbCommand)connection.CreateCommand();
command.CommandText = "select 'A' union select 'B'";
using (command)
{
using (DbDataReader reader = command.ExecuteReader())
{
IEnumerable<string> columnNames = null;
while (reader.Read())
{
if (columnNames == null)
columnNames = GetColumnNames(reader);
results.Add(new EevaDynamicRecord(columnNames, reader));
}
//foreach (DbDataRecord record in reader)
//{
// if (columnNames == null)
// columnNames = GetColumnNames(record);
// yield return new EevaDynamicRecord(columnNames, record);
//}
}
}
}
return results;
}
I have the following code
[DataContract]
public enum StatusType
{
[EnumMember(Value = "A")]
All,
[EnumMember(Value = "I")]
InProcess,
[EnumMember(Value = "C")]
Complete,
}
I'd like to do the following:
var s = "C";
StatusType status = SerializerHelper.ToEnum<StatusType>(s); //status is now StatusType.Complete
string newString = SerializerHelper.ToEnumString<StatusType>(status); //newString is now "C"
I've done the second part using DataContractSerializer (see code below), but it seems like a lot of work.
Am I missing something obvious? Ideas? Thanks.
public static string ToEnumString<T>(T type)
{
string s;
using (var ms = new MemoryStream())
{
var ser = new DataContractSerializer(typeof(T));
ser.WriteObject(ms, type);
ms.Position = 0;
var sr = new StreamReader(ms);
s = sr.ReadToEnd();
}
using (var xml = new XmlTextReader(s, XmlNodeType.Element, null))
{
xml.MoveToContent();
xml.Read();
return xml.Value;
}
}
Here is my proposition - it should give you the idea on how to do this (check also Getting attributes of Enum's value):
public static string ToEnumString<T>(T type)
{
var enumType = typeof (T);
var name = Enum.GetName(enumType, type);
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
return enumMemberAttribute.Value;
}
public static T ToEnum<T>(string str)
{
var enumType = typeof(T);
foreach (var name in Enum.GetNames(enumType))
{
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
if (enumMemberAttribute.Value == str) return (T)Enum.Parse(enumType, name);
}
//throw exception or whatever handling you want or
return default(T);
}
If your project references Newtonsoft.Json (what doesn't these days?!), then there is a simple one line solution that doesn't need reflection:
public static string ToEnumString<T>(T value)
{
return JsonConvert.SerializeObject(value).Replace("\"", "");
}
public static T ToEnum<T>(string value)
{
return JsonConvert.DeserializeObject<T>($"\"{value}\"");
}
The ToEnumString method will only work if you have the StringEnumConverter registered in your JsonSerializerSettings (see JavaScriptSerializer - JSON serialization of enum as string), e.g.
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Converters = { new StringEnumConverter() }
};
Another advantage of this method is that if only some of your enum elements have the member attribute, things still work as expected, e.g.
public enum CarEnum
{
Ford,
Volkswagen,
[EnumMember(Value = "Aston Martin")]
AstonMartin
}
Using extensions and C# 7.3 constraints
public static class EnumMemberExtensions
{
public static string ToEnumString<T>(this T type)
where T : Enum
{
var enumType = typeof(T);
var name = Enum.GetName(enumType, type);
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
return enumMemberAttribute.Value;
}
public static T ToEnum<T>(this string str)
where T : Enum
{
var enumType = typeof(T);
foreach (var name in Enum.GetNames(enumType))
{
var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
if (enumMemberAttribute.Value == str) return (T)Enum.Parse(enumType, name);
}
//throw exception or whatever handling you want or
return default;
}
}
You can use reflection to get the value of the EnumMemberAttribute.
public static string ToEnumString<T>(T instance)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("instance", "Must be enum type");
string enumString = instance.ToString();
var field = typeof(T).GetField(enumString);
if (field != null) // instance can be a number that was cast to T, instead of a named value, or could be a combination of flags instead of a single value
{
var attr = (EnumMemberAttribute)field.GetCustomAttributes(typeof(EnumMemberAttribute), false).SingleOrDefault();
if (attr != null) // if there's no EnumMember attr, use the default value
enumString = attr.Value;
}
return enumString;
}
Depending on how your ToEnum works, you might want to use this sort of approach there as well. Also, the type can be inferred when calling ToEnumString, e.g. SerializerHelper.ToEnumString(status);
This example shows how to convert enums using the DescriptionAttribute, the EnumMemberAttribute and the property name:
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
public static class EnumExtensions
{
public static T ToEnumByAttributes<T>(this string value)
where T:Enum
{
var enumType = typeof(T);
foreach (var name in Enum.GetNames(enumType))
{
var field = enumType.GetField(name);
if(field == null) continue;
var enumMemberAttribute = GetEnumMemberAttribute(field);
if (enumMemberAttribute != null && enumMemberAttribute.Value == value)
{
return (T)Enum.Parse(enumType, name);
}
var descriptionAttribute = GetDescriptionAttribute(field);
if (descriptionAttribute != null && descriptionAttribute.Description == value)
{
return (T)Enum.Parse(enumType, name);
}
if (name == value)
{
return (T)Enum.Parse(enumType, name);
}
}
throw new ArgumentOutOfRangeException(nameof(value), value, $"The value could not be mapped to type {enumType.FullName}");
}
public static string ToStringByAttributes(this Enum value)
{
var field = value
.GetType()
.GetField(value.ToString());
if (field == null) return string.Empty;
var enumMemberAttribute = GetEnumMemberAttribute(field);
if (enumMemberAttribute != null)
{
return enumMemberAttribute.Value ?? string.Empty;
}
var descriptionAttribute = GetDescriptionAttribute(field);
if (descriptionAttribute != null)
{
return descriptionAttribute.Description;
}
return value.ToString();
}
private static DescriptionAttribute? GetDescriptionAttribute(FieldInfo field)
{
return field
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.OfType<DescriptionAttribute>()
.SingleOrDefault();
}
private static EnumMemberAttribute? GetEnumMemberAttribute(FieldInfo field)
{
return field
.GetCustomAttributes(typeof(EnumMemberAttribute), false)
.OfType<EnumMemberAttribute>()
.SingleOrDefault();
}
}
NUnit Tests:
[TestFixture]
public sealed class EnumExtensionsTests
{
public enum TestEnum
{
[EnumMember(Value = "A")]
Alpha,
[Description("O")]
Omega
}
[Test]
public void ShouldSerialize_FromEnumAttribute()
{
var result = TestEnum.Alpha.ToStringByAttributes();
Assert.That(result, Is.EqualTo("A"));
}
[Test]
public void ShouldSerialize_FromDescriptionAttribute()
{
var result = TestEnum.Omega.ToStringByAttributes();
Assert.That(result, Is.EqualTo("O"));
}
[Test]
public void ShouldDeserialize_FromEnumAttribute()
{
var result = "A".ToEnumByAttributes<TestEnum>();
Assert.That(result, Is.EqualTo(TestEnum.Alpha));
}
[Test]
public void ShouldDeserialize_FromDescriptionAttribute()
{
var result = "O".ToEnumByAttributes<TestEnum>();
Assert.That(result, Is.EqualTo(TestEnum.Omega));
}
[Test]
public void ShouldDeserialize_FromPropertyName()
{
var result = "Alpha".ToEnumByAttributes<TestEnum>();
Assert.That(result, Is.EqualTo(TestEnum.Alpha));
}
}
I need to read the attribute of a property using reflection
For example I get the following :
[XmlElement("Id")]
[CategoryAttribute("Main"), ReadOnly(true),
Description("This property is auto-generated")]
[RulesCriteria("ID")]
public override string Id
{
get { return _ID; }
set
{
_ID = value;
}
}
i want to get the " read only "value of this property using reflection
can anybody help
It's difficult to write the code for your case without knowing the Type name. Hope below example helps.
using System;
using System.Reflection;
public class Myproperty
{
private string caption = "Default caption";
public string Caption
{
get{return caption;}
set {if(caption!=value) {caption = value;}
}
}
}
class Mypropertyinfo
{
public static int Main(string[] args)
{
Console.WriteLine("\nReflection.PropertyInfo");
// Define a property.
Myproperty Myproperty = new Myproperty();
Console.Write("\nMyproperty.Caption = " + Myproperty.Caption);
// Get the type and PropertyInfo.
Type MyType = Type.GetType("Myproperty");
PropertyInfo Mypropertyinfo = MyType.GetProperty("Caption");
// Get and display the attributes property.
PropertyAttributes Myattributes = Mypropertyinfo.Attributes;
Console.Write("\nPropertyAttributes - " + Myattributes.ToString());
return 0;
}
}
MSDN
public static bool PropertyReadOnlyAttributeValue(PropertyInfo property)
{
ReadonlyAttribute attrib = Attribute.GetCustomAttribute(property, typeof(ReadOnlyAttribute));
return attrib != null && attrib.IsReadOnly;
}
public static bool PropertyReadOnlyAttributeValue(Type type, string propertyName)
{
return PropertyReadOnlyAttributeValue(type.GetProperty(propertyName));
}
public static bool PropertyReadOnlyAttributeValue(object instance, string propertyName)
{
if (instance != null)
{
Type type = instance.GetType();
return PropertyReadOnlyAttributeValue(type, propertyName);
}
return false;
}
The following code gives me the errors:
Cannot implicitly convert type T to string.
Cannot implicitly convert type T to int.
What do I have to do to get this method to return the type of variable I define with T when I call it?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestGener234
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("his first name is {0}", GetPropertyValue<string>("firstName"));
Console.WriteLine("his age is {0}", GetPropertyValue<int>("age"));
}
public static T GetPropertyValue<T>(string propertyIdCode)
{
if (propertyIdCode == "firstName")
return "Jim";
if (propertyIdCode == "age")
return 32;
return null;
}
}
}
Addendum:
Here is a more complete example of why I needed the generic solution, i.e. I have a class that saves its values as strings no matter what the type, and this generic solution simply makes the calling code cleaner:
using System;
using System.Collections.Generic;
namespace TestGener234
{
class Program
{
static void Main(string[] args)
{
List<Item> items = Item.GetItems();
foreach (var item in items)
{
string firstName = item.GetPropertyValue<string>("firstName");
int age = item.GetPropertyValue<int>("age");
Console.WriteLine("First name is {0} and age is {1}.", firstName, age);
}
Console.ReadLine();
}
}
public class Item
{
public string FirstName { get; set; }
public string Age { get; set; }
public static List<Item> GetItems()
{
List<Item> items = new List<Item>();
items.Add(new Item { FirstName = "Jim", Age = "34" });
items.Add(new Item { FirstName = "Angie", Age = "32" });
return items;
}
public T GetPropertyValue<T>(string propertyIdCode)
{
if (propertyIdCode == "firstName")
return (T)(object)FirstName;
if (propertyIdCode == "age")
return (T)(object)(Convert.ToInt32(Age));
return default(T);
}
}
}
That is troublesome; to make the compiler happy you can double-cast, but that implies a box for value types:
public static T GetPropertyValue<T>(string propertyIdCode)
{
if (propertyIdCode == "firstName")
return (T)(object)"Jim";
if (propertyIdCode == "age")
return (T)(object)32;
return default(T);
}
In reality, I think you may be better just using an object return type.
This is an abuse of generics. If you have a small number of types that the generic type parameter could possibly be then just replace it with that many methods:
string GetTextProperty(string propertyName) { ... }
int GetNumberProperty(string propertyName) { ... }
Giraffe GetGiraffeProperty(string propertyName) { ... }
This should work...
public static T GetPropertyValue<T>(string propertyIdCode)
{
if (propertyIdCode == "firstName")
return (T)Convert.ChangeType("Jim", typeof(T));
if (propertyIdCode == "age")
return (T)Convert.ChangeType(22, typeof(T));
return default(T);
}
GetPropertyValue<string>("age") wants to return a string. Change it to GetPropertyValue<int>("age") and it will work as long as "age" is your parameter value.
Your implementation would be better off getting the type of the generic parameter T in order to choose what to return instead of basing it on the function parameter.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestGener234
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("his first name is {0}", GetPropertyValue<string>("firstName"));
Console.WriteLine("his age is {0}", GetPropertyValue<int>("age"));
}
public static T GetPropertyValue<T>(string propertyIdCode)
{
if (typeof(T) == typeof(string) && propertyIdCode == "firstName")
return "Jim";
if (typeof(T) == typeof(string) && propertyIdCode == "age")
return "32";
if (typeof(T) == typeof(int) && propertyIdCode == "age")
return 32;
throw (new ArgumentException());
}
}
}
You can return object from GetPropertyValue and then do a cast. You are trying to use a generic method to return specific types depending on input parameters. Sounds confusing :-)
public static object GetPropertyValue(string propertyIdCode)
{
if (propertyIdCode == "firstName")
return "Jim";
if (propertyIdCode == "age")
return 32;
return null;
}
and then cast (int)GetPropertyValue("age");
Usually when you are casting inside a generic method, it is a design problem. Usually, you want to keep your type generic inside your method (no casting, no braching based on type), something like this:
public class Properties<T>
{
private Dictionary<string, T> _dict = new Dictionary<string, T>();
public void SetPropertyValue<T>(string propertyIdCode, T value)
{
_dict[propertyIdCode] = value;
}
public T GetPropertyValue<T>(string propertyIdCode)
{
return _dict[propertyIdCode];
}
}
On, the other hand, if you want to access object's properties through their name (it seems like this is what you are doing, sorry if I got it wrong), the right way would be to use reflection (PropertyInfo.GetValue):
public object GetPropertyValue(object obj, string propertyIdCode)
{
PropertyInfo pinfo = obj.GetType().GetProperty(propertyIdCode);
return pinfo.GetValue(obj, null);
}
public static T GetPropertyValue<T>(string propertyIdCode)
{
object result = null;
if (propertyIdCode == "firstName")
result = "Jim";
if (propertyIdCode == "age")
result = 32;
return result == null ? default(T) : (T)result;
}
Marc's example of double-casting is the correct way to get the compiler to behave correctly.
You could write a sperate method for each value type and have a generic method for reference types. This would stop stop boxing on value types.
This is only useful if the objects being accessed are not boxed for storage (e.g. not stored as an object).
public static T GetPropertyValue<T>(string propertyIdCode)
{
}
public static int GetPropertyInt(string propertyIdCode)
{
}
There is another way - by using Convert.ChangeType:
CType(Convert.ChangeType(mItem, GetType(TItem)), TItem)