Cast generics based on condition - c#

I am trying to return derived class, but for some implementations I have to provide a little bit more setup, so I have if, but the compiler is not smart enough to know that once it enters the if, it should be able to cast T to Derived1. Is there any other way to do it?
Cannot convert type 'Derived1' to type T
public T GetData<T>(string name) where T : Data
{
if (_cache.ContainsKey(name))
return (T)_cache[name];
using (var db = new LiteDatabase(_dbPath))
{
if (typeof(T) is ChartData)
{
var collection = db.GetCollection<ChartData>("Data");
var chunksColl = db.GetCollection<ValuesChunk>(nameof(ValuesChunk));
var data = collection.Include(d => d.Series).FindOne(Query.EQ(nameof(ChartData.Name), name));
foreach (var series in data.Series)
{
for (int index = 0; index < series.Chunks.Count; index++)
{
var chunk = chunksColl.FindById(series.Chunks[index].ChunkId);
series.ChangeChunk(index, chunk);
}
}
_cache.Add(name, data);
return (Data)data;
}
else
{
var collection = db.GetCollection<T>();
return collection.FindOne(Query.EQ(nameof(ChartData.Name), name));
}
}
}
public abstract class Data {/*properties*/}
public class ChartData : Data {/*properties*/}
public class ScriptData : Data {/*properties*/}

Couple of mistakes in the above, this works (whether its the best design is a different question):
public T Get<T>() where T : Base
{
if (typeof(T) == typeof(Derived1)) //correction to this line
{
var derived = new Derived1();
derived.Property1 = 5;
return derived as T; //correction to this line
}
else
{
// return (T)myMethod;
}
return null;
}
or with your edited version:
public T GetData<T>(string name) where T : Data
{
if (_cache.ContainsKey(name))
return (T)_cache[name];
using (var db = new LiteDatabase(_dbPath))
{
if (typeof(T) == typeof( ChartData)) //<======CORRECTION HERE
{
var collection = db.GetCollection<ChartData>("Data");
var chunksColl = db.GetCollection<ValuesChunk>(nameof(ValuesChunk));
var data = collection.Include(d => d.Series).FindOne(Query.EQ(nameof(ChartData.Name), name));
foreach (var series in data.Series)
{
for (int index = 0; index < series.Chunks.Count; index++)
{
var chunk = chunksColl.FindById(series.Chunks[index].ChunkId);
series.ChangeChunk(index, chunk);
}
}
_cache.Add(name, data);
return data as Data;//<======CORRECTION HERE
}
else
{
var collection = db.GetCollection<T>();
return collection.FindOne(Query.EQ(nameof(ChartData.Name), name));
}
}
}

Related

Modify multiple fields of an object in an Enumerable at the same time in C#

If I have an Enumerable of objects and want to modify multiple fields of a single one that I already know the index, I currently do:
var myObject = myEnumerable[index];
myObject.one = 1;
myObject.two = 2;
Is there a way to compact that? To make it simpler?
As an example, in VB you can do:
With myEnumerable[index]
.one = 1
.two = 2
End With
PS: using doesn't work here as the object would need to implement IDisposable, we don't' always control the object. I'm looking for a generic way to do this.
Handmade way of obtaining what you wanted. You can use it on every object.
class Program
{
static void Main(string[] args)
{
var item = new Item
{
One = 0,
Two = 0
};
item.SetProperties(new string[] { "One", "Two" }, new object[] { 1, 2 });
}
}
public class Item
{
public int One { get; set; }
public int Two { get; set; }
}
public static class Extensions
{
public static void SetProperties<T>(this T obj, IEnumerable<string> propertiesNames, IEnumerable<object> propertiesValues)
{
if (propertiesNames.Count() != propertiesValues.Count())
{
throw new ArgumentNullException();
}
var properties = obj.GetType().GetProperties();
for (int i = 0; i < propertiesNames.Count(); i++)
{
var property = properties.FirstOrDefault(x => x.Name == propertiesNames.ElementAt(i));
if (property is null)
{
throw new ArgumentException();
}
property.SetValue(obj, propertiesValues.ElementAt(i));
}
}
public static void SetFields<T>(this T obj, IEnumerable<string> fieldsNames, IEnumerable<object> fieldsValues)
{
if (fieldsNames.Count() != fieldsValues.Count())
{
throw new ArgumentNullException();
}
var fields = obj.GetType().GetFields();
for (int i = 0; i < fieldsNames.Count(); i++)
{
var field = fields.FirstOrDefault(x => x.Name == fieldsNames.ElementAt(i));
if (field is null)
{
throw new ArgumentException();
}
field.SetValue(obj, fieldsValues.ElementAt(i));
}
}
}

Generic function to get data from SqlDataReader

I'm creating a generic application to get all the data from different SQL Server tables.
I have a generic function that convert the SqlDataReader to a list:
public static List<T> MapToList<T>(this SqlDataReader dr) where T : new()
{
List<T> RetVal = null;
var Entity = typeof(T);
var PropDict = new Dictionary<string, PropertyInfo>();
try
{
if (dr != null && dr.HasRows)
{
RetVal = new List<T>();
var Props = Entity.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropDict = Props.ToDictionary(p => p.Name.ToUpper(), p => p);
while (dr.Read())
{
T newObject = new T();
for (int Index = 0; Index < dr.FieldCount; Index++)
{
if (PropDict.ContainsKey(dr.GetName(Index).ToUpper()))
{
var Info = PropDict[dr.GetName(Index).ToUpper()];
if ((Info != null) && Info.CanWrite)
{
var Val = dr.GetValue(Index);
Info.SetValue(newObject, (Val == DBNull.Value) ? null : Val, null);
}
}
}
RetVal.Add(newObject);
}
}
}
catch (Exception)
{
throw;
}
return RetVal;
}
Now suppose I have this class for my data:
public partial class User
{
public int Id { get; set; }
public string Name { get; set; }
}
I can fetch my data from the table something like this:
const string GetAreasQuery = "select id, name from dbo.user";
SqlDataReader dr = DoQueryToDB(GetAreasQuery);
List<User> userList = dr.MapToList<User>();
Now, I have n different classes like User (Classroom and so on) and I don't want to write the code above for each class I have. I would like to create a generic GetData to retrieve those information:
public List<T> GetData<T> (string Query_)
{
SqlDataReader dr = DataReader(Query_);
List<T> data = new List<T>();
data = dr.MapToList<T>();
return data;
}
where T can be User, Classroom and so on...
I tried this solution but I always have to specify the type:
public object GetData(string Query_, Type type)
{
SqlDataReader dr = DataReader(Query_);
if (type == typeof(User))
{
List<User> data = new List<User>();
data = dr.MapToList<User>();
return data;
}
else if (..)
{}
return null;
}
I'm trying different possibilities but I always obtain an error in GetData<T> function. More precisely in MapToList<T> like: T should be a non abstract type or public constructor without parameters.
You should add a contraint to the method GetData in order to achieve the same constraint level that is found in MapToList, which requires T to have a empty constructor
public List<T> GetData<T>(string Query_) where T : new()
{
SqlDataReader dr = DataReader(Query_);
return dr.MapToList<T>();
}

Get all properties and subproperties from a class

I am using reflection to get a class name, and need to get all sub properties of the class, and all the sub properties' properties.
I am running into a recursion issue where the items get added to the incorrect list.
My code is as follows:
private List<Member> GetMembers(object instance)
{
var memberList = new List<Member>();
var childMembers = new List<Member>();
foreach (var propertyInfo in instance.GetType().GetProperties())
{
var member = new Member
{
Name = propertyInfo.PropertyType.IsList() ? propertyInfo.Name + "[]" : propertyInfo.Name,
Type = SetPropertyType(propertyInfo.PropertyType),
};
if (propertyInfo.PropertyType.IsEnum)
{
member.Members = GetEnumValues(propertyInfo).ToArray();
}
if (propertyInfo.PropertyType.BaseType == typeof(ModelBase))
{
var childInstance = propertyInfo.GetValue(instance) ?? Activator.CreateInstance(propertyInfo.PropertyType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.ToArray();
}
if (propertyInfo.PropertyType.IsGenericType && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>) ||
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
var itemType = propertyInfo.PropertyType.GetGenericArguments()[0];
var childInstance = Activator.CreateInstance(itemType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.Distinct().ToArray();
}
memberList.Add(member);
}
return memberList;
}
I can't know for certain since I don't have the knowledge of your code to debug and test it; however, I believe your problem may be stemming from the fact that you're re-using the childMembers list. Let me know if this is not the case.
private List<Member> GetMembers(object instance)
{
var memberList = new List<Member>();
foreach (var propertyInfo in instance.GetType().GetProperties())
{
var childMembers = new List<Member>(); // Moved to here, so it's not shared among all propertyInfo iterations.
var member = new Member
{
Name = propertyInfo.PropertyType.IsList() ? propertyInfo.Name + "[]" : propertyInfo.Name,
Type = SetPropertyType(propertyInfo.PropertyType),
};
if (propertyInfo.PropertyType.IsEnum)
{
member.Members = GetEnumValues(propertyInfo).ToArray();
}
if (propertyInfo.PropertyType.BaseType == typeof(ModelBase))
{
var childInstance = propertyInfo.GetValue(instance) ?? Activator.CreateInstance(propertyInfo.PropertyType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.ToArray();
}
if (propertyInfo.PropertyType.IsGenericType && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>) ||
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
var itemType = propertyInfo.PropertyType.GetGenericArguments()[0];
var childInstance = Activator.CreateInstance(itemType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.Distinct().ToArray();
}
memberList.Add(member);
}
return memberList;
}
Wouldn't the following do?
public static IEnumerable<PropertyInfo> GetProperties(this Type type, int depth = 1)
{
IEnumerable<PropertyInfo> getProperties(Type currentType, int currentDepth)
{
if (currentDepth >= depth)
yield break;
foreach (var property in currentType.GetProperties())
{
yield return property;
foreach (var subProperty in getProperties(property.PropertyType,
currentDepth + 1))
{
yield return subProperty;
}
}
}
if (depth < 1)
throw new ArgumentOutOfRangeException(nameof(depth));
return getProperties(type, 0);
}
Given the following type:
class Foo
{
public string S { get; }
public int I { get; }
}
The output of
Console.WriteLine(string.Join(Environment.NewLine,
typeof(Foo).GetProperties(2)
.Select(p => $"{p.DeclaringType.Name}: {p.Name}")));
would be:
Foo: S
String: Chars
String: Length
Foo: I

How can I remove duplication between methods that differ only on type but use non-default constructors?

Hello I got many methods (below 2 examples) that look almost exactly the same. The difference is in name of JSON breanch that is processed, type of returned list and type of objects added to list. I know these example methods yet needs some optimization in its body, but the case is to pass type of returned value and type of class which method currently need and make it all work. If it is possible I would like to avoid casting in place of calling method.
Method 1
public static List<Box> JsonToListOfBoxes(string data)
{
List<Box> ListOfBoxes = new List<Box>();
if(!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson["boxes"];
if(jtkProduct != null)
if(jtkProduct.HasValues)
{
int childrenCount = productsJson["boxes"].Count();
for(int x = 0;x < childrenCount;x++)
ListOfBoxes.Add(new Box(productsJson["boxes"][x]));
}
}
return ListOfBoxes;
}
Method 2
public static List<Envelope> JsonToListOfEnvelopes(string data)
{
List<Envelope> ListOfEnvelopes = new List<Envelope>();
if(!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson["envelopes"];
if(jtkProduct != null)
if(jtkProduct.HasValues)
{
int childrenCount = productsJson["envelopes"].Count();
for(int x = 0;x < childrenCount;x++)
ListOfEnvelopes.Add(new Envelope(productsJson["envelopes"][x]));
}
}
return ListOfEnvelopes;
}
Using generics you can change as follows : (without parameterized generic constructor)
public static List<T> JsonToListOfEnvelopes<T>(string data, string searchString, Func<string, T> creator)
{
List<T> ListOfEnvelopes = new List<T>();
if (!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson[searchString];
if (jtkProduct != null)
if (jtkProduct.HasValues)
{
int childrenCount = productsJson[searchString].Count();
for (int x = 0; x < childrenCount; x++)
ListOfEnvelopes.Add(creator(productsJson[searchString][x]));
}
}
return ListOfEnvelopes;
}
And you can call it as
var result = JsonToListOfEnvelopes("data", "boxes", c => { return new Box(c); });
var result = JsonToListOfEnvelopes("data", "envelopes", c => { return new Envelope(c); });
You could make generic method where dataName should be "boxes" or "envelopes":
public static List<T> JsonToListOfBoxes<T>(string data, string dataName)
{
List<T> ListOfItems = new List<T>();
if (!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson[dataName];
if (jtkProduct != null)
if (jtkProduct.HasValues)
{
int childrenCount = productsJson[dataName].Count();
for (int x = 0; x < childrenCount; x++)
ListOfItems.Add((T)Activator.CreateInstance(typeof(T), productsJson[dataName][x]));
}
}
return ListOfItems;
}
Use example:
var list1 = JsonToListOfBoxes<Box>("dataString", "boxes");
var list2 = JsonToListOfBoxes<Envelope>("dataString", "envelopes");
I just changed #msmolcic logic a bit.
public static List<T> JsonToListOfBoxes<T>(string data)
{
List<T> ListOfItems = new List<T>();
string dataName = typeof(T) == typeof(Box) ? "boxes" : "envelopes";
//if there are many types one can try in below way..
// if (typeof(T) == typeof(Box))
// {
// dataName = "Box";
// }
// else if (typeof(T) == typeof(Envelope))
// {
// dataName = "envelopes";
// }
if (!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson[dataName];
if (jtkProduct != null)
if (jtkProduct.HasValues)
{
int childrenCount = productsJson[dataName].Count();
for (int x = 0; x < childrenCount; x++)
ListOfItems.Add((T)Activator.CreateInstance(typeof(T), productsJson[dataName][x]));
}
}
return ListOfItems;
}

C# - Class with List<> of other Classes

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;
}

Categories

Resources