I have a helper method that accepts parameters with generic types. This method can throw exceptions, which I log in order to debug later. Some objects have ids, some have names, etc., that would be useful to know when trying to debug. How can I log this information in a useful way?
Regardles if it is a generic method or not, unless your object is null, you can log the type with myObject.GetType().ToString()
Loger.log("Object type that has thrown an error is" + myObj.GetType().ToString());
EDIT:
If you would like to read more information from the object then you can read object properties like this:
Type type = myObj.GetType();
List<string> info = new List<String>();
info.Add("Object type that has thrown an error is: " + type.ToString() + Environment.NewLine);
foreach (var prop in type.GetProperties())
{
if (prop.CanRead)
{
info.Add("Name: " + prop.Name + " , Value: " + prop.GetValue(myObj) + Environment.NewLine);
}
}
Logger.log(string.Join("", info));
This should work without any problems for primitives. For Objects it depends on how the ToString method is implemented. Also note that it will not read recursively, just the direct members of the class.
This function returns a C# style type name (something like List<String> or List<Dictionary<Int32,String>>) from a type.
public static string FancyTypeName(Type type)
{
var typeName = type.Name.Split('`')[0];
if (type.IsGenericType)
{
typeName += string.Format("<{0}>", string.Join(",",
type.GetGenericArguments().Select(v => FancyTypeName(v)).ToArray())
);
}
return typeName;
}
Related
So I'm wanting to return the properties of an object, either generic or hard coded typeof(User) for e.g
However i only want to return the properties where the object I'm getting the properties for, has a value set against it, not the default value nor null. The reason for this is so that i can use these properties only to build an expression to only check these properties against columns in our database for items.
I tried something like this, however it still brings back all the values,
public User AutomatedUser {get;set;} // some properties of this will populated elsewhere
var props = typeof(User).GetProperties()
.Where(pi => pi.GetValue(AutomatedFromUser) != pi.PropertyType.GetDefault());
I then found this method on the forum for getting default values of types, as compiler won't allow != default(pi.PropertyType) as "Pi" is a variable. Method below...
public static object GetDefault(this Type type)
{
// If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null
if (type == null || !type.IsValueType || type == typeof(void))
return null;
// If the supplied Type has generic parameters, its default value cannot be determined
if (type.ContainsGenericParameters)
throw new ArgumentException(
"{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> contains generic parameters, so the default value cannot be retrieved");
// If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct), return a
// default instance of the value type
if (type.IsPrimitive || !type.IsNotPublic)
{
try
{
return Activator.CreateInstance(type);
}
catch (Exception e)
{
throw new ArgumentException(
"{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe Activator.CreateInstance method could not " +
"create a default instance of the supplied value type <" + type +
"> (Inner Exception message: \"" + e.Message + "\")", e);
}
}
// Fail with exception
throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> is not a publicly-visible type, so the default value cannot be retrieved");
}
}
Any tips or help would be greatly appreciated as to why this wouldn't be working, or where i'm going wrong.
The problem you are having is related to boxing and the fact that == performs reference equality. Both PropertyInfo.GetValue and your GetDefault function return object, so your value types will be boxed. This means that even if both values are zero, they will be placed into two seperate boxes. Each of those boxes is a different object and thus reference equality returns false.
Consider the following:
object x = 0;
object y = 0;
Console.WriteLine(x == y); // prints False
The solution is to call object.Equals (either the instance or static version) instead.
object x = 0;
object y = 0;
Console.WriteLine(x.Equals(y)); // prints True
Console.WriteLine(object.Equals(x, y)); // prints True
See this SharpLab demo for an example of both versions.
This means that the solution to your question is the following:
var props = typeof(User).GetProperties()
.Where(pi =>
!object.Equals(
pi.GetValue(AutomatedFromUser),
pi.PropertyType.GetDefault()
)
);
We use the static version to guard ourselves against null since null.Equals(...) would obviously throw. Calling the static method is the same as the instance method except that it first checks for reference equality and then guards against nulls. After that it calls x.Equals(y).
I am working on a web application that prints various computer parts on the user's screen which they can make a choice out of, with the appropriate price and link. I use MongoDB to store the data, and I use a generic class to dynamically choose the appropriate class (each implement IProduct and have unique properties).
Consider this method:
public HtmlString DatabaseResult<T>(string collectionName)
where T : IProduct
{
var collection = db.GetCollection<T>(collectionName);
var buildString = "";
var query =
from Product in collection.AsQueryable<T>()
where Product.Prijs == 36.49
orderby Product.Prijs
select Product;
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (T item in query){
buildString = buildString + "<p>";
foreach (PropertyInfo property in properties)
{
buildString = buildString + " " + item.property; //Error Here
}
buildString = buildString + "</p>";
}
HtmlString result = new HtmlString(buildString);
return result;
}
Where I am trying to loop through the properties of a class that implements IProduct. Each class that does so have 4 properties in common, and 3 properties that differ. This is why I need to programmatically loop through the properties. I realized that using a reflection to use properties on an actual class is not going to work. Here is my error (the error occurs where I commented in the above method)
'T' does not contain a definition for 'property' and no extension method 'property' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
the result should be something like:
"<p>"
+(value of Motherboard.Price) (value of Motherboard.Productname)
(value of Motherboard.Productlink) value of Motherboard.YetAnotherAttribute).... etc+
"</p>"
Is my desired method of doing this even possible? I'm looking for a solution to my problem, even possibly an entire redesign of my code where necessary. Thanks in advance for answering.
Change
buildString = buildString + " " + item.property;
To
buildString = buildString + " " + property.GetValue(item, null).ToString();
//or
buildString = String.Format("{0} {1}", buildString, property.GetValue(item, null));
PropertyInfo.GetValue doesn't need the second paramater as of .NET 4.5, I believe
In c#, can you do something like this?
type typeOfInt = type(int);
int x = (typeOfInt)1;
Basically I want to store a type as a variable, so I can use the variable as a cast.
I am trying to do this because in me parametric polymorphism function, a variable could be 1 of 2 types. Both types has the same methods I want to use, but it wont let me use it because it's of a variable type.
public static void SetFolderOrderValue<T>(T item, int value, Report reportObject)
{
if (!item.Exists)
{
reportObject.Log("The folder \"" + item.Name + "\" does not exist.");
return;
}
try
{
item.SetProperty(folderOrderProperty, value);
item.Update();
}
catch (SPException ex)
{
reportObject.Log("An error occurred when saving changes to the folder \"" + item.Name + "\". Maybe due to concurrent changes from another user. Please try again after a minute.\n" + Report.GetErrorInfo(ex));
}
catch (Exception ex)
{
reportObject.Log("An error occured with \"" + item.Name + "\":\n" + Report.GetErrorInfo(ex));
}
}
If I can at least store the cast as a value, then I can just pass another boolean in the function saying which of the 2 types it is.
You are talking about polymorphism, so use it.
If you have 2 types with same or common methods and you have a function or set of functions to act on them, define an interface that describes that set of methods shared between those 2 types and :
If the types you're talking about are named SPFile and SPFolder
public class SPFile : IMyNewInterface {
.....
}
public class SPFolder : IMyNewInterface {
...
}
public static void SetFolderOrderValue<T>(T item, int value,
Report reportObject) where T : IMyNewInterface {
...
}
No, you can't do this.
My answer assumes that Tigran's answer doesn't apply, maybe because you can't change the types in question.
The following would be one way to achieve what you want to do:
var varOfTypeOne = item as TypeOne;
if(varOfTypeOne != null)
{
varOfTypeOne.CallMethod();
return;
}
var varOfTypeTwo = item as TypeTwo;
if(varOfTypeTwo != null)
{
varOfTypeTwo.CallMethod();
return;
}
Another way would be to use the DLR via dynamic:
dynamic dynamicItem = item;
dynamicItem.CallMethod();
This will fail at runtime if the actual type of item doesn't define CallMethod, so you are basically losing compile time safety.
BTW: A void method that takes a generic parameter without constraint usually can simply be replaced by a method that is non-generic and has object as the parameter type.
Yes you can save the type but this is probably a code smell.
Read up on http://msdn.microsoft.com/en-us/library/system.object.gettype.aspx
and http://msdn.microsoft.com/en-us/library/system.type.aspx
Here is an example
int x = 1;
var type = x.GetType();
Console.WriteLine(type);
I have the following code where I'm trying to get all the properties of an object as well as the property values. Some properties can be collections or collections of collections, so I tried to set up a recursive function for those types. Unfortunately it's not working, erroring on this line
if (property.GetValue(item, null) is IEnumerable)
and I don't know what needs changed. Can anyone help here? Thanks.
public static string PackageError(IEnumerable<object> obj)
{
var sb = new StringBuilder();
foreach (object o in obj)
{
sb.Append("<strong>Object Data - " + o.GetType().Name + "</strong>");
sb.Append("<p>");
PropertyInfo[] properties = o.GetType().GetProperties();
foreach (PropertyInfo pi in properties)
{
if (pi.GetValue(o, null) is IEnumerable && !(pi.GetValue(o, null) is string))
sb.Append(GetCollectionPropertyValues((IEnumerable)pi.GetValue(o, null)));
else
sb.Append(pi.Name + ": " + pi.GetValue(o, null) + "<br />");
}
sb.Append("</p>");
}
return sb.ToString();
}
public static string GetCollectionPropertyValues(IEnumerable collectionProperty)
{
var sb = new StringBuilder();
foreach (object item in collectionProperty)
{
PropertyInfo[] properties = item.GetType().GetProperties();
foreach (var property in properties)
{
if (property.GetValue(item, null) is IEnumerable)
sb.Append(GetCollectionPropertyValues((IEnumerable)property.GetValue(item, null)));
else
sb.Append(property.Name + ": " + property.GetValue(item, null) + "<br />");
}
}
return sb.ToString();
}
I would suggest using an existing serialization mechanism, such as XML serialization or JSON serialization, to provide this information if you are trying to make it generic.
It sounds like that particular property is an indexer, so it's expecting you to pass index values to the GetValue method. There is no easy way to take an indexer and determine what values are valid to pass as indices, in the general case, as a class is free to implement an indexer however it wants. For instance, a Dictionary keyed on a string has an indexer by key, which can take indices as enumerated in the Keys property.
The typical approach to serializing collections is to handle them as a special case, treating each primitive collection type (array, List, Dictionary, etc.) individually.
Note that there is a difference here between a property returning IEnumerable and an indexer.
I'm having trouble grasping reflection in C#, so I'm going to put my specific situation down and see what you guys can come up with. I've read TONS of C# reflection questions on here, but I still just simply don't get it.
So here's my situation; I'm trying to access an array which is a non-public member of a class I have access to.
Basically it's a System.Collections.CollectionBase which has an array variable called "list", but it has this parent type of OrderCollection and the reflection of it is just confusing the hell out of me.
I have to do a lot of these so a good guide or example would really help. Please let me know if you would like more information.
I blacked out the name of the namespace not because what I'm doing is not illegal by any means, but I'm trying to be first to market on this and so I'm trying to be careful.
What are you trying to use reflection at all? CollectionBase supports indexing, but only through the explicit interface implementation of IList, so you ought to be able to write:
IList list = Acct.Orders;
response = list[0];
You may need to cast the result to a more appropriate type, but I don't see any need for reflection here.
EDIT: Original answer didn't take account of explicit interface implementation.
While this may not help you, it may help others. Here is a simplified example of reflection:
using System;
using System.Reflection;
namespace TeamActivity
{
class Program
{
static void Main(string[] args)
{
// Dynamically load assembly from the provided DLL file.
Assembly CustomerAssembly = Assembly.LoadFrom( "BasicCalculations.dll" );
// Get a Type from the Assembly
Type runtimeType = CustomerAssembly.GetType( "BasicCalcuation.BasicCalc" );
// Get all methods from the Type.
MethodInfo[] methods = runtimeType.GetMethods();
// Loop through all discovered methods.
foreach ( MethodInfo method in methods )
{
Console.WriteLine( "Method name: " + method.Name );
// Create an array of parameters from this method.
ParameterInfo[] parameters = method.GetParameters();
// Loop through every parameter.
foreach ( ParameterInfo paramInfo in parameters )
{
Console.WriteLine( "\tParamter name: " + paramInfo.Name );
Console.WriteLine( "\tParamter type: " + paramInfo.ParameterType );
}
Console.WriteLine( "\tMethod return parameter: " + method.ReturnParameter );
Console.WriteLine( "\tMethod return type: " + method.ReturnType );
Console.WriteLine("\n");
}
// Invoke the Type that we got from the DLL.
object Tobj = Activator.CreateInstance( runtimeType );
// Create an array of numbers to pass to a method from that invokation.
object[] inNums = new object[] { 2, 4 };
// Invoke the 'Add' method from that Type invokation and store the return value.
int outNum = (int)runtimeType.InvokeMember( "Add", BindingFlags.InvokeMethod, null, Tobj, inNums );
// Display the return value.
Console.WriteLine( "Output from 'Add': " + outNum );
Console.WriteLine( "\nPress any key to exit." );
Console.ReadKey();
}
}
}