Access nested Object fields - c#

I want to access the field names of an object which ist nested into a structure as follows:
public class Playerframe
{
public string Attr1;
public string Attr2;
}
public class MatchMoment
{
public int MomentNr;
public Dictionary <int, Playerframe> PlayerData;
}
public DataTable CreateTable (List<dynamic>Moments)
{
DataTable table = new DataTable();
List<string> = Moments[0]......
/// get all class Properties, including the Playerframe properties in order
/// to create correctly named DataTable columns
/// The List<string> should finally contain {"MomentNr","Attr1","Attr2"}
return table;
}
My question now would be how to access the field names("e.g. Attr1") stored in the Dictionary value within a MatchMoment object object using System.Reflection?
I want to write a function which creates a datatable object out of the properties of any given object which is defined in the method parameter, as shown above.
Thx for your help!
Max

I think the following snippet might get you what you want. Basically, it iterates over the properties of the element type of the list to get their names, and in case of a generic property type, recursively gets the names of the properties of the generic type arguments.
public DataTable CreateTable(List<dynamic> Moments)
{
var table = new DataTable();
var elementType = GetElementType(Moments);
var propertyNames = GetPropertyNames(elementType);
// Do something with the property names . . .
return table;
}
private static Type GetElementType(IEnumerable<dynamic> list) =>
list.GetType().GetGenericArguments()[0];
private static IEnumerable<string> GetPropertyNames(Type t)
{
return t.GetProperties().SelectMany(getPropertyNamesRecursively);
IEnumerable<string> getPropertyNamesRecursively(PropertyInfo p) =>
p.PropertyType.IsGenericType
? p.PropertyType.GetGenericArguments().SelectMany(GetPropertyNames)
: new[] { p.Name };
}
Note that this only looks at properties, and your current classes exclusively use fields. However, the use of properties is considered best practice for public access to data, so it might be worthwhile to change your fields to properties. If you really want to keep them as fields, you might have to tweak it a bit, but the idea of recursively unfolding generic types remains the same.

Related

How do i fill a list with objects that are passed to a class(2nd class) from another class(3rd class)?

public class E
{
public static List<Tranzactie> tranzactie = new List<Tranzactie>();
}
public class Tranzactie
{
public string name;
public string contract;
}
static void main()
{
where i have a method with a loop that parses a.txt file
and passes extracted values to Tranzactie
}
In class E there is a list i want to populate with values extracted from parsing a txtfile in Main. The data collected in Main fills fields from class Tranzactie.
How do i put the values passed to "Tranzactie" into the list found in class E?
Well, somewhere in your loop, you'll need to:
instantiate a new Tranzactie object
populate the name and contract fields
add this object to your List<Tranzactie>
So, here's the code to do just that part:
// assuming you have variables "name" and "contract" that hold necessary data
var tranzactie = new Tranzactie()
{
name = name,
contract = contract
};
E.tranzactie.Add(tranzactie);
As an aside, I would rename the List<Tranzactie> in class E to tranzactii, because it represents multiple transactions - a collection - so plural form of the word is typically preferred.

Getting repeated fields via inheritance with GetFields()

I have a problem while working with GetFields() method while trying to get all the fields from a user defined class.
I have a parent class with, lets say two public fields, and another class that inherits from the parent and overrides one of the parent fields with the new keyword.
Problem comes when I use the GetFields method with the second class, because it's returning all the fields, including the overriden one. Is there something that I am doing wrong or a solution for only getting in this case the GoodBye field from the Parent class and the only one Hello field, the one from the inherited class?
This is a simple example of what I'm saying:
public class Foo
{
public string Hello = "sdfsafda";
public string GoodBye = string.Empty;
}
public class Bar : Foo
{
public new string Hello = "jl";
}
static void Main(string[] args)
{
var a = new Bar();
var derp = new List<string>();
foreach (var fieldInfo in a.GetType().GetFields())
{
derp.Add(fieldInfo.Name);
}
Console.WriteLine(derp.Count);
// writes 3 instead of 2
}
About using Properties instead of Fields, I'm afraid I can't use them right now because of limitations that are not in my hand to solve in the project I'm working on.
It seems there is no pre-collected information about shadowed fields available. If you just need a simple workaround, you can first get the names of all fields without duplicates and then use GetField(fieldName) to only get the most derived field instance:
var type = a.GetType();
foreach (var field1 in type.GetFields().Select(x => x.Name).Distinct())
{
var fieldInfo = type.GetField(field1);
derp.Add(fieldInfo.Name);
Console.WriteLine(fieldInfo.DeclaringType);
Console.WriteLine(fieldInfo);
}
ofcourse, if you only need the field names like in your question code, instead of the FieldInfo object, you can stop after the .Select(x => x.Name).Distinct().

Filling Array with Custom Defined Type in C#

When attempt to fill an array in C# with my custom type class, the array populates with the class's location in the namespace. However, I need the array to populate with the values I specified with that type.
I have tried defining my array using each of these implementations with the same results:
List<FieldName> fieldNames = new List<FieldName>() { "event_ref", "user_id", "sys_time" };
FieldName[] fieldNames = { "event_ref", "user_id", "sys_time" };
In both cases the values that get implemented in the lower function calls (nothing fancy) is ElectronicSJP.DatabaseIF.FieldName for all three of the fields.
Upon setting a breakpoint, I can see that fieldNames is populated as so:
The values that I specified are at the next level down, if that table is expanded. My class for FieldName is defined as such:
class FieldName
{
private string name = "";
public static implicit operator string(FieldName fn)
{
return (fn == null) ? null : fn.name;
}
public static implicit operator FieldName(string fn)
{
return new FieldName { name = fn };
}
}
I suspect that I may need to add another implicit operator, or two, so I could use some help with that or any other ideas you think could fix this problem. Thanks.
When the debugger shows an object, or list of objects, it will show the type rather than a particular field.
In order to see a "user friendly value", one can either override the toString method that exists on the Object class, or you can decorate the property you want shown with the DebuggerDisplay Attribute (credit to Adam V up above in the comments!)
example:
public override string ToString()
{
return name;
}
Link to how to override a method. https://msdn.microsoft.com/en-us/library/ms173154.aspx

How do I return a class that inherits a generic list?

I have this class:
public class CampaignMaps : List<CampaignMap> { };
The CampaignMap object is my of my own and does not inherit anything. It has your standard properties and methods.
I then have a CampaignMapAdapter object which acts as the data to my ObjectDatasource. It has one private property for the data:
[DataObject(true)]
public class CampaignMapAdapter
{
private Database db = new Database(
ConfigurationManager.AppSettings["AppName"],
ConfigurationManager.AppSettings["DBService"],
ConfigurationManager.AppSettings["DBUser"]);
private CampaignMaps _data
{
get
{
CampaignMaps maps = new CampaignMaps();
CampaignsAdapter adapter = new CampaignsAdapter();
db.Init();
db.AddParameter(null, null, ParameterDirection.ReturnValue, OracleType.Cursor);
DataSet ds = db.ExecuteDataSet(PROC_GET_CAMPAIGN_MAPS);
DataTable dt = ds.Tables[0];
foreach (DataRow row in dt.Rows)
{
CampaignMap campaignMap = new CampaignMap();
//populate the campaignMap object...
maps.Add(campaignMap);
}
return maps;
}
set
{
_data = value;
}
}
[DataObjectMethod(DataObjectMethodType.Select, true)]
public CampaignMaps GetFiltered(bool hasErrors)
{
var selectQuery = from c in _data where c.HasError == hasErrors select c;
_data = selectQuery;
}
}
_data = selectQuery; is throwing the error:
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable' to
'CampaignMaps'. An explicit conversion exists (are you missing a
cast?)
I suppose in plain English I want _data to always contain ALL my data elements, then calling a particular select should whittle them down as desired. How do I go about doing this?
Thanks in advance.
Well, you could do:
CampaignMaps maps = new CampaignMaps();
maps.AddRange(selectQuery);
_data = maps;
That would get you the right data in the right types.
However, I would strongly consider not deriving from List<T> to start with - it's almost never a good idea; prefer composition over inheritance unless you're really specializing the behaviour.
I'd also say that mutating the current object in a method called GetFiltered violates the principle of least surprise. I would either change it to make it clearer that it's a mutation (e.g. "FilterByErrors") and give it a void return type, or just make it return the filtered list without mutating the current object.
Why are you doing _data = selectQuery;?
I would think your intention in the GetFiltered method would be something like:
return new CampaignMaps(selectQuery);
However, like Adam said, I would seriously consider why you are using a CampaignMaps class.
If you want the public interface to only allow filtering by "HasErrors", make the GetFiltered method look like this:
public IEnumerable<CampaignMap> GetFiltered(bool hasErrors)
{
return _data.Where(c => c.HasError == hasErrors);
}
I don't see any reason why you'd want to have this GetFiltered method in the first place. Why not expose the _data property as a get-only IEnumerable<CampaignMap> and then allow other objects to run Linq queries on it directly? Like this:
public IEnumerable<CampaignMap> Data
{
get { return _data; }
}
Then somewhere else I can just write Data.Where(c => c.HasError == whatever).

C#: DataTable conversion row-by-row

In an abstract class (C# 3), there is a virtual method named GetData which returns a DataTable. The algorithm of the method is as following:
Get SQL query string from the class.
Get DataTable from database using above query.
Do transformations on DataTable and return it.
In the 3rd point, I clone the original DataTable in order to change the Type of column (I can't do that on populated table and can't setup this at the 2nd point) and enumerate every row in which I copy and transform data from the original one. Transformations depends on the class, every one has private methods which transforms data on its own.
The question is: How to make a method in a base class which will be based on the following three params: column name which will be converted, Type of new column and action during the transformation. Two first are quite simple but I'm not sure about the third one. I thought about designing a new class which will store these three parameters, and the action will be stored as following delegate:
public delegate object Convert(object objectToConvert);
The conversion would look something like that:
int rowCounter = 0;
foreach(DataRow row in dt.Rows)
{
foreach(var item in Params)
{
row[item.ColumnIndex] = item.Convert(originalDataTable.Rows[rowCounter].ItemArray[item.ColumnIndex]);
}
++rowCounter;
}
For now, I have to override the method GetData() in every class I want to have a transformations which causes a large duplication of code. The point I want to achieve is to make a base class which will be based on params I mentioned above. Is the above solution good for this problem or is it any other way to do that?
First I'm not a big fan of DataTable, you can use DataReader instead if you do a foreach.
using (var dataReader = ....)
{
while(dataReader.Read())
{
foreach(var item in Params)
{
row[item.ColumnIndex] = item.Convert(originalDataTable.Rows[rowCounter].ItemArray[item.ColumnIndex]);
}
}
}
Second, make a base class with a abstract method with some code and make the object in the list Params inherits of this class and override the method only when it necessary.
public class MyClass
{
public abstract object Convert(DataRow row)
{
....
}
}
public class foo : MyClass
{
}
Okay, for now I have a following solution which does fully satisfy me:
Every convertor implements following interface:
public interface IConvertor
{
object Convert(object item);
}
Params class is as following:
Type ColumnType { get; protected set; } // New type
string[] ColumnNames { get; protected set; }
IConvertor Convertor { get; protected set; }
Every object have its own Params array property. I've got one method for every derived class and all what I have to do is to set up parameters.

Categories