I want to create static class which is generic. That class should pass through each property and check if is array. If result is true, than class should check how many elements are present in class and to return that number.
What I had did till now:
public static class Helper<T> where T : class
{
private static int _counter = 0;
public static int Counter()
{
Type type = typeof(T);
foreach (var property in type.GetProperties())
{
if (property.PropertyType.IsArray)
{
}
}
return _counter;
}
}
I need help how to get number of present elements in array.
If you also want to use it on a instanced object instead of an Type you could do s.th. like this (remove the generic type of Helper and make the Counter method generic):
public static class Helper
{
// This method will only iterate the public static properties
public static int Counter<T>() where T : class => Counter(typeof(T), null);
// This method will iterate all public properties
public static int Counter<T>(T objectToCount) where T : class
{
if(objectToCount == null)
{
throw new ArgumentNullException(nameof(objectToCount));
}
return Counter(typeof(T), objectToCount);
}
public static int Counter(Type type, object instance)
{
int _counter = 0;
PropertyInfo[] properties = null;
if(instance == null)
{
properties = type.GetProperties(BindingFlags.Static | BindingFlags.Public);
}
else
{
properties = type.GetProperties();
}
foreach (var property in properties)
{
if (property.PropertyType.IsArray)
{
var array = property.GetValue(instance, null) as Array;
var length = array?.Length;
// do s.th. with your counter
}
}
return _counter;
}
}
then you could use it like:
Helper.Counter(x);
Helper.Counter<TestClass>();
Helper.Counter<TestClass>(x);
Update:
for only instanced objects it could be simplified to this:
public static int Counter(object objectToCount)
{
if(objectToCount == null)
{
throw new ArgumentNullException(nameof(objectToCount));
}
int _counter = 0;
foreach (var property in objectToCount.GetType().GetProperties())
{
if (property.PropertyType.IsArray)
{
var array = property.GetValue(objectToCount, null) as Array;
var length = array?.Length;
// do s.th. with your counter
}
}
return _counter;
}
If I get your question right, you want to get the number of array properties of a class.
in this case, you could choose two approaches.
extention method:
public static class Extensions
{
public static int NumbeofArays<TClass>(this TClass entry) where TClass : class, new()
{
Type type = typeof(TClass);
int arrays = 0;
foreach (var propertyInfo in type.GetProperties())
{
if (propertyInfo.PropertyType.IsArray)
arrays = arrays + 1;
}
return arrays;
}
}
Or a Helper Class:
public static class ClassHelper<TClass> where TClass : class, new()
{
static ClassHelper()
{
Type type = typeof(TClass);
int arrays = 0;
foreach (var propertyInfo in type.GetProperties())
{
if (propertyInfo.PropertyType.IsArray)
arrays = arrays + 1;
}
NumberofArrays = arrays;
}
public static int NumberofArrays { get; }
}
public static int Counter()
{
Type type = typeof(T);
foreach (var property in type.GetProperties())
{
Console.WriteLine(property.Name);
if (property.PropertyType.IsArray)
{
var array = property.GetValue(null, null) as Array;
Console.WriteLine(array.Length);
}
}
return _counter;
}
example: https://dotnetfiddle.net/2ICron
Related
In C# is it possible to convert an array type to singular - for use with Activator.CreateInstance. Take this for example:
void Main()
{
var types = new[] { typeof(ExampleClass), typeof(ExampleClass[]) };
var objects = new List<object>();
foreach (var type in types)
{
// possibly convert type here? (from array to singular - type[] to type)
Debug.WriteLine($"{type}");
objects.Add(Activator.CreateInstance(type));
}
}
// Define other methods and classes here
public class ExampleClass
{
public int X;
public int Y;
}
Gets the following output:
If I understand your Question right you might want something like this using Type.GetElementType() via reflection.
static void Main(string[] args)
{
var types = new[] { typeof(ExampleClass), typeof(ExampleClass[]) };
var objects = new List<object>();
foreach (var type in types)
{
var typeInstance = type.GetElementType();
if (typeInstance != null)
{
Debug.WriteLine($"{typeInstance}");
objects.Add(Activator.CreateInstance(typeInstance));
}
else
{
objects.Add(Activator.CreateInstance(type));
}
}
}
public class ExampleClass
{
public int X;
public int Y;
}
If I understand your question correctly, you want to get the base-type of an array, right? That should be quite easy with the IsArray property of the type, simply check each entry of your list like this:
private static Type GetTypeOrElementType(Type type)
{
if (!type.IsArray)
return type;
return type.GetElementType();
}
Btw, if you want to create a new Array of that specific type, you can use Array.CreateInstance instead of Activator.CreateInstance
Found this works:
void Main()
{
var types = new[] { typeof(ExampleClass), typeof(ExampleClass[]) };
var objects = new List<object>();
foreach (var type in types)
{
Debug.WriteLine($"{type}");
objects.Add(type.IsArray
? Activator.CreateInstance(type, 1)
: Activator.CreateInstance(type));
}
}
// Define other methods and classes here
public class ExampleClass
{
public int X;
public int Y;
}
In this scenario I can't create a base type for both TypeA and TypeB to derive from so I cannot add a Where : TypeBase on the method. I would like to see if there is another way to solve this.
class TypeA
{
int number;
string someString;
}
class TypeB
{
int number;
decimal someDecimal;
}
private List<int> Test(object inputs)
{
List<int> rv = new List<int>();
if (inputs is List<TypeA>)
inputs = (List<TypeA>)inputs;
else if(inputs is List<TypeB>)
inputs = (List<TypeB>)inputs;
foreach(item in inputs)
{
rv.Add(item.number);
}
return rv;
}
private void GenericTest<T>(IEnumerable<T> inputs)
{
MethodInfo property = typeof(T).GetProperty(/*YOUR PROPERTY*/).GetMethod;
foreach (var input in inputs)
{
object value = property.Invoke(input, null);
// Logic
}
}
// To protect it from error
public void Test(IEnumerable<TypeA> inputs)
{
GenericTest(inputs);
}
// To protect it from error
public void Test(IEnumerable<TypeB> inputs)
{
GenericTest(inputs);
}
And by the way why not pass the property name to the Test method ?
You can cast it to an IEnumerable and then do the foreach-loop :
private void Test(object inputs)
{
// you can also change the parameter to IEnumerable to take advantage of compile-time type-checking.
var items = inputs as IEnumerable;
if (items == null)
throw new ArgumentException("inputs");
foreach (var item in items)
{
}
}
If the logic needs to be different for each class, use this :
private void Test(object inputs)
{
var typeAs = inputs as IEnumerable<TypeA>;
if (typeAs != null)
{
foreach (var item in typeAs)
{
}
return;
}
var typeBs = inputs as IEnumerable<TypeB>;
if (typeBs != null)
{
foreach (var item in typeBs)
{
}
return;
}
throw new ArgumentException("inputs");
}
You will do no additional work if you iterate over them independently:
class Program
{
private void Test(object inputs)
{
if (inputs is List<TypeA>)
loop((List<TypeA>) inputs);
if (inputs is List<TypeB>)
loop((List<TypeB>)inputs);
}
private void loop(IEnumerable<TypeA> inputs)
{
foreach (var input in inputs)
{
logic(input.i);
}
}
private void loop(IEnumerable<TypeB> inputs)
{
foreach (var input in inputs)
{
logic(input.i);
}
}
private void logic(int i)
{
// magic happens here
}
}
class TypeA
{
public int i;
}
class TypeB
{
public int i;
}
But for the love of all that is good in the world, please don't do this.
If you really must iterate over the lists in one go for some reason, and you know that Test will only be called with TypeAs and TypeBs, you can handle this with a dynamic type:
class Program
{
static void Main(string[] args)
{
var w = new TypeA {i = 8};
var x = new TypeA {i = 16};
var y = new TypeB {i = 32};
var z = new TypeC(); // don't pass this to Test!
var l = new List<dynamic> {w, x, y};
Test(l);
}
private static void Test(IEnumerable<dynamic> inputs)
{
foreach (var input in inputs)
{
logic(input.i);
}
}
private static void logic(int i)
{
// magic happens here
}
}
class TypeA
{
public int i;
}
class TypeB
{
public int i;
}
class TypeC {} // no members
I need to list all properties for containing class, I have a lot of them, so I don't know the type in the code, but I can get it trough prop.BastType.FullName, but I can't cast it, and I don't want to change all my classes to crate "Cast" methods everywere.
To avoid printing Property Properties such as DeclaringType,ReflectedType, etc, I'm excluding them in the code, but I don't like this way...
if (("DeclaringType,ReflectedType,MetadataToken,Module,PropertyType,Attributes,CanRead" +
",CanWrite,GetMethod,SetMethod,IsSpecialName,CustomAttributes,MemberType")
.Contains(prop.Name))
continue;
full code of Method:
private static void GetAllPropertiesFor(object oo, int level, List<KeyValuePair<string,string>> result)
{
Type entType = oo.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(entType.GetProperties());
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(oo, null);
if (("DeclaringType,ReflectedType,MetadataToken,Module,PropertyType,Attributes,CanRead" +
",CanWrite,GetMethod,SetMethod,IsSpecialName,CustomAttributes,MemberType")
.Contains(prop.Name))
continue;
if (propValue == null)
{
result.Add(new KeyValuePair<string, string>((new string(' ', level * 2)) + prop.Name, "Object"));
//GetAllPropertiesFor(prop, level + (1*3), result);
}
else
{
if (result == null) result = new List<KeyValuePair<string, string>>();
result.Add(new KeyValuePair<string, string>((new string(' ', level)) + prop.Name, propValue.ToString()));
}
if (entType.Namespace == "System.Data.Entity.DynamicProxies" && entType.BaseType.FullName.StartsWith("RE2012.Data.Models."))
{
GetAllPropertiesFor(prop, level + 1, result);
}
// Do something with propValue
}
}
any suggestions?
this way I don't have in results, the values of:
PropertyType : System.Int32
Attributes : None
CanRead : True
CanWrite : True
just what I need.
Disclaim i can't really work out what you want so this answer is just a shot in the dark
Based on what i think i understood you want to get the Properties from a specific Class so here is my example:
First my same Inheritance
class BaseClass
{
public int Base { get; set; }
}
class ChildClass : BaseClass
{
public double Child { get; set; }
}
class ChildChildClass : ChildClass
{
public string ChildChild { get; set; }
}
now lets find the single Properties
class Program
{
static void Main(string[] args)
{
var obj = new ChildChildClass();
var ChildChildType = obj.GetType();
var p = new Program();
// here we get the ChildClass properties
var t = p.getBaseType(ChildChildType, 1);
Console.WriteLine(t.Name);
p.getproperties(t);
// here we get the BaseClass properties
t = p.getBaseType(ChildChildType, 2);
Console.WriteLine(t.Name);
p.getproperties(t);
// here we get the Object properties
t = p.getBaseType(ChildChildType, 3);
Console.WriteLine(t.Name);
p.getproperties(t);
Console.Read();
}
internal Type getBaseType(Type t, int level)
{
Type temp ;
for (int i = 0; i < level; i++)
{
temp = t.BaseType;
if(temp == null)
throw new ArgumentOutOfRangeException("you are digging to deep");
else
t = temp;
}
return t;
}
private void getproperties(Type t)
{
PropertyInfo[] properties = t.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
Console.WriteLine(property.Name);
}
Console.WriteLine("");
}
}
if you want some information about BindingFlags here is a Link
I have a class, that has several elements of normal types, like int, String, etc.
It also has several elements that are various lists of other classes, that could be empty or have 1 to many items.
I have a function that I call with a generic type of the parent class, and I want to analyze data that could be in the sub elements, without knowing the types.
I am getting the parent members with the following code:
var getProperty = System.Runtime.CompilerServices.
CallSite<Func<System.Runtime.CompilerServices.CallSite,
object, object>>
.Create(Microsoft.CSharp.RuntimeBinder.
Binder.GetMember(0, property.Name, thisObject.GetType(), new[]
{
Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null)
}));
var thisValue = getProperty.Target(getProperty, thisObject);
I get the value into the var thisValue. At this point if I determine the underlying type of thisValue is a type of list, how can I grab the type of the list contents?
Here is the actual function....I can't seem to get it formatted nicely.
public static bool ObjectIsLike<T>(this T thisObject, T compareObject, params object[] argumentsToExclude)
{
for (int counter = 0; counter < argumentsToExclude.Length - 1; counter++)
{
argumentsToExclude[counter] = argumentsToExclude[counter].ToString().ToUpper();
}
bool objectIsLike = true;
foreach (var property in thisObject.GetType().GetProperties())
{
string fieldName = property.Name;
if (!argumentsToExclude.Contains(fieldName.ToUpper()))
{
try
{
var getProperty = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, property.Name, thisObject.GetType(), new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) }));
var thisValue = getProperty.Target(getProperty, thisObject);
getProperty = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, property.Name, compareObject.GetType(), new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) }));
var compareValue = getProperty.Target(getProperty, compareObject);
if (!(compareValue == null && thisValue == null))
{
if (compareValue == null || thisValue == null)
objectIsLike = false;
else
if (compareValue.GetType().FullName.Contains("List"))
{
//Ignore Lists
}
else
if (!compareValue.Equals(thisValue))
{
objectIsLike = false;
}
}
}
catch
{
objectIsLike = false;
}
}
}
return objectIsLike;
}
would GetType() work for you?
class Program
{
static void Main(string[] args)
{
MyClass1 c1 = new MyClass1();
foreach (var s in c1.pp)
{
Console.WriteLine(s.GetType());
}
Console.Read();
}
}
public class MyClass1
{
public MyClass2 p;
public List<object> pp;
public MyClass1()
{
p = new MyClass2();
pp = new List<object>();
pp.Add(new MyClass2());
pp.Add(new MyClass3());
pp.Add(new MyClass4());
}
}
public class MyClass2
{
public List<object> ppp;
public MyClass2()
{
ppp = new List<object>();
ppp.Add(new MyClass3());
ppp.Add(new MyClass4());
}
}
public class MyClass3
{
public int v;
}
public class MyClass4
{
public int v;
}
Apologies for the amount of code, but it is easier to explain it this way.
I have a custom attribute CustomUserData implemented as follows:
public class CustomUserData : Attribute
{
public CustomUserData(object aUserData)
{
UserData = aUserData;
}
public object UserData { get; set; }
}
and an extension method for enums as:
public static class EnumExtensions
{
public static TAttribute GetAttribute<TAttribute>(this Enum aValue) where TAttribute : Attribute
{
Type type = aValue.GetType();
string name = Enum.GetName(type, aValue);
return type.GetField(name)
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
public static object GetCustomUserData(this Enum aValue)
{
CustomUserData userValue = GetAttribute<CustomUserData>(aValue);
return userValue != null ? userValue.UserData : null;
}
}
I then have a helper class that serializes/deserializes an enum that has custom data associated with it as follows:
public static class ParameterDisplayModeEnumListHelper
{
public static List<ParameterDisplayModeEnum> FromDatabase(string aDisplayModeString)
{
//Default behaviour
List<ParameterDisplayModeEnum> result = new List<ParameterDisplayModeEnum>();
//Split the string list into a list of strings
List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(','));
//Iterate the enum looking for matches in the list
foreach (ParameterDisplayModeEnum displayModeEnum in Enum.GetValues(typeof (ParameterDisplayModeEnum)))
{
if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0)
{
result.Add(displayModeEnum);
}
}
return result;
}
public static string ToDatabase(List<ParameterDisplayModeEnum> aDisplayModeList)
{
string result = string.Empty;
foreach (ParameterDisplayModeEnum listItem in aDisplayModeList)
{
if (result != string.Empty)
result += ",";
result += listItem.GetCustomUserData();
}
return result;
}
}
however this is specific to ParameterDisplayModeEnum and I have a bunch of enums that I need to treat this way for serialization/deserialization so I would prefer to have a generic such as:
public static class EnumListHelper<TEnum>
{
public static List<TEnum> FromDatabase(string aDisplayModeString)
{
//Default behaviour
List<TEnum> result = new List<TEnum>();
//Split the string list into a list of strings
List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(','));
//Iterate the enum looking for matches in the list
foreach (TEnum displayModeEnum in Enum.GetValues(typeof (TEnum)))
{
if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0)
{
result.Add(displayModeEnum);
}
}
return result;
}
public static string ToDatabase(List<TEnum> aDisplayModeList)
{
string result = string.Empty;
foreach (TEnum listItem in aDisplayModeList)
{
if (result != string.Empty)
result += ",";
result += listItem.GetCustomUserData();
}
return result;
}
}
However this will not work as GetCustomUserData() cannot be invoked. Any suggestions? I cannot change the use of the custom attribute or the use of the enums. I am looking for a generic way to do the serialization/deserialization without having to write a concrete list helper class each time.
All suggestions appreciated.
Try this code:
public static class EnumListHelper
{
private static void EnsureIsEnum<TEnum>()
{
if (!typeof(TEnum).IsEnum)
throw new InvalidOperationException(string.Format("The {0} type is not an enum.", typeof(TEnum)));
}
public static List<TEnum> FromDatabase<TEnum>(string aDisplayModeString)
where TEnum : struct
{
EnsureIsEnum<TEnum>();
//Default behaviour
List<TEnum> result = new List<TEnum>();
//Split the string list into a list of strings
List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(','));
//Iterate the enum looking for matches in the list
foreach (Enum displayModeEnum in Enum.GetValues(typeof(TEnum)))
{
if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0)
{
result.Add((TEnum)(object)displayModeEnum);
}
}
return result;
}
public static string ToDatabase<TEnum>(List<TEnum> aDisplayModeList)
where TEnum : struct
{
EnsureIsEnum<TEnum>();
string result = string.Empty;
foreach (var listItem in aDisplayModeList.OfType<Enum>())
{
if (result != string.Empty)
result += ",";
result += listItem.GetCustomUserData();
}
return result;
}
}
var fromDatabase = EnumListHelper.FromDatabase<TestEnum>("test");
EnumListHelper.ToDatabase(fromDatabase);
UPDATE 0
To be clear, because we cannot restrict generics to Enum we should check that the type TEnum is an enum and throw an exception if it is not.
When we use the FromDatabase method we know that TEnum is enum, and we can write this code to cast an enum to the specified TEnum:
result.Add((TEnum)(object)displayModeEnum)
in the ToDatabase method we also know that TEnum is enum and we can write this code to convert TEnum to the Enum type:
aDisplayModeList.OfType<Enum>()
Ideally you'd want to restrict TEnum to Enum but that won't work as you can not restrict generics to Enum MicrosoftBut try following, it might do the trick...
if (listOfDisplayModes.FindIndex(item =>
item == (string)(displayModeEnum as Enum).GetCustomUserData()) >= 0)