Given two properties in class A:
class A
{
public virtual List<string> BaseStrings{get;}
public virtual List<string> MyStrings{get;}
}
class B:A
{
public override List<string> BaseStrings{//baseA.BaseStrings+baseA.MyStrings}
public override List<string> MyStrings{//?}
}
class C:B
{
public override List<string> BaseStrings{//baseB.BaseStrings+baseB.MyStrings}
public override List<string> MyStrings{//?}
}
How do I craft the properties so that the BaseStrings accumulate in each derived class? I don't want to redeclare that structure in each derived class. I just want to ask for BaseStrings for the accumulated base strings.
EDIT: Based on some of the comments, the below classes achieve the goal. What pitfalls will I come across plastering new everywhere? It seems as if I cast D to any of it's previous forms like C, I will get exactly the set of strings I expect to see for a C. Yes!
I was hoping there was a way to write this and not be so redundant...
public abstract class A
{
public virtual List<string> BaseStrings { get; } = new List<string>();
public virtual List<string> MyStrings { get; } = new List<string>();
}
public class B : A
{
public new List<string> BaseStrings => base.BaseStrings.Concat(base.MyStrings).ToList();
public new List<string> MyStrings => new List<string>() { "dog", "cat" };
}
public class C : B
{
public new List<string> BaseStrings => base.BaseStrings.Concat(base.MyStrings).ToList();
public new List<string> MyStrings => new List<string>() { "Fox", "Hare" };
}
public class D : C
{
public new List<string> BaseStrings => base.BaseStrings.Concat(base.MyStrings).ToList();
public new List<string> MyStrings =>new List<string>{"Tortise","Fish"};
}
[I] want each class to have a property that contains only it's strings, and a separate property that gives [me] it's strings plus it's parents' strings (recursively)
I don't think inheritance is the right way to do this - it sounds like you want encapsulation (a C has a B, which has a A). With inheritance, you have one instance that you can treat as either an A, a B, or a C. Different instances will not share the lists with each other.
That said, just for fun, you could do it by using new properties (not overridden) for each type:
class A
{
public List<string> BaseStrings {
get {
return this.MyStrings.ToList();
}
}
public List<string> MyStrings{get; set;}
}
class B:A
{
public new List<string> BaseStrings {
get {
return base.BaseStrings.Concat(this.MyStrings).ToList();
}
}
public new List<string> MyStrings{get; set;}
}
class C:B
{
public new List<string> BaseStrings {
get {
return base.BaseStrings.Concat(this.MyStrings).ToList();
}
}
public new List<string> MyStrings{get; set;}
}
Usage:
C c = new C();
B b = c;
A a = c;
c.MyStrings = new List<string> {"C"};
b.MyStrings = new List<string> {"B"};
a.MyStrings = new List<string> {"A"};
Console.WriteLine(string.Join(", ", a.MyStrings));
Console.WriteLine(string.Join(", ", a.BaseStrings));
Console.WriteLine(string.Join(", ", b.MyStrings));
Console.WriteLine(string.Join(", ", b.BaseStrings));
Console.WriteLine(string.Join(", ", c.MyStrings));
Console.WriteLine(string.Join(", ", c.BaseStrings));
Output:
A
A
B
A, B
C
A, B, C
When aggregating values, don't use lists, as creating a new list for every sub class creates an extra useless iteration and allocation. Instead for purposes of aggregation, use IEnumerable<T>.
I think what you want is something like so:
class A
{
public virtual IEnumerable<string> GetStrings() {
yield return "Base String A";
yield return "Base String B";
}
}
class B:A
{
public override IEnumerable<string> GetStrings() {
foreach (string b in base.GetStrings())
yield return b;
yield return "My String C";
yield return "My String D";
}
}
class C:B
{
public override IEnumerable<string> GetStrings() {
foreach (string b in base.GetStrings())
yield return b;
yield return "My String E";
yield return "My String F";
}
}
If you really insist on using List properties, you can do that while still aggregating with IEnumerable, the below would work. Just remember that since you make MyStrings virtual, any implementation that overrides that now takes full ownership and potentially erases all previously held values in any sub classes. So C's overrid of MyStrings can completely wipe out all of B's held MyStrings values. this is why I would always prefer the first solution provided with IEnumerables all the way.
class A
{
public virtual List<string> BaseStrings { get; }
public virtual List<string> MyStrings { get; }
public virtual IEnumerable<string> GetStrings() {
foreach (string b in BaseStrings)
yield return b;
foreach (string m in MyStrings)
yield return m;
}
}
class B:A
{
public override List<string> MyStrings {//?}
}
class C:B
{
public override List<string> MyStrings {//?}
}
Now any time you want to grab all the strings, just use item.GetStrings()
Related
I have following object structure where B is derived from A and I am getting input as List<A> with lots of records. I want to convert thatList<A> to List<B> with easy steps (without looping). What is the best way to achieve the same.
Note: I don't want to use AutoMapper.
public class A
{
public A() { }
public virtual string Name
{
get;
set;
}
}
public class B : A
{
public B()
: base()
{
}
private string _name;
public override string Name
{
get
{
return _name;
}
set
{
_name = string.Concat("Hello ", base.Name);
}
}
public string Id
{
get { return "101"; }
}
}
You can do this by declaring constructor for class B from class A
in this way:
public B(A a):base()
{
this._name = a.Name;
}
And than do this:
var listA = new List<A> { new A { Name = "John" }, new A { Name = "Peter" }, new A { Name = "Julia" } };
List<B> listB = listA.Select(x=> new B(x)).ToList();
Based on the comments this is the output, though I still don't really understand the point.
List<A> thisIsA = new List<A>();
thisIsA.Add(new B());
List<B> thisIsB = new List<B>();
thisIsB.AddRange(thisIsA.Cast<B>());
I have the following-
public Class A
{
public int a;
public List<String> list;
}
public class B
{
public int a;
public string b;
}
A instanceofA;
B someB = new B()
{
a = 1,
b = "SomeString"
};
instanceofA= new A()
{
a = someB.a,
list = new List(){B}
};
return instanceofA;
The reason why i am converting string to List<String> is to pass it on to the UI layer and render it as drop down menu.
How to get the string b to the List<String> variable?
B.ToList() converts B into a character array.
Do you want to put the b-property of your B-class into a list? Then you need the b-member to be added to the list, not your instance of B.
A instanceofA;
B someB= new B()
{
a=1,
b="SomeString"
};
instanceofA = new A()
{
a=someB.a,
list = new List<string> {someB.b }
};
You need to add the string member b to the list, not the entire class instance of B (which is not a string, because it is a B).
public Class A
{
public int a;
public List<String> list = new List<String>();
}
public Class B
{
public int a;
public string b;
}
public A someMethod()
{
B someB = new B()
{
a=1,
b="SomeString"
};
A instanceofA = new A();
instanceofA.a = someB.a;
instanceofA.list.add(someB.b);
return instanceofA;
}
You can do this(This question is not clear)
public class A{
public A()
{
this.list=new List<string>();
}
public List<String> list;
}
then A.list.Add();
If you wants add b to A list
public class A{
public List<B> BList;
}
A.BList.Add();
Here is a sample code :
class Program
{
public class A : I {
string a = "myClassA";
// whatever
}
public class B : I
{
string b = "myClassB";
// whatever
}
public interface I
{
// whatever
}
public static void myFunction<T>(List<T> list)
{
Console.WriteLine(list[0].GetType().Name); // OUTPUT : A
Console.WriteLine(JsonConvert.SerializeObject(list[0])); // OUTPUT : {}
}
static void Main(string[] args)
{
List<I> myList = new List<I>();
myList.Add(new A());
myFunction(myList); // arguments mismatch ERROR
}
}
I need this function to output a JSON with appropriate fields (here it should be {"a":"myClassA"}
How should I do ?
EDIT
My real context :
List<string> JSONElements = new List<string>();
List<I> myList = null;
getDataInList(ref myList);
listToJSON(myList, ref JSONElements);
// ...
public void getDataInList(ref List<I> theList) {
// insert data in list
// every elements will be A or every elements will be B
}
// ...
public void listToJSON(List<I> myList, ref List<string> JSONElements) {
foreach (var element in myList) {
// I want every field to be included
// interface has no fields
JSONElements.Add(JsonConvert.SerializeObject(element));
}
}
This function "listToJSON" wont serialize correctly (every elements will be {})
How can I fix this ?
Can you keep
public void myFunction(List<I> list)
(as I not T) and then change
List<A> myList = new List<A>();
to
List<I> myList = new List<I>();
?
In reply to your edit following is printed out by the code at the bottom:
{"Word":"hello","Number":1}
{"Word":"goodbye","Number":2}
Done, press any key...
I am using Newtonsoft.JSon version 6.0.0.0 from the NuGet feed at https://www.nuget.org/api/v2/.
However, when I change public string Word { get; set; } to string Word { get { return "Hello"; } } then Word is not serialised. I don't know how the newtonsoft JSON serialiser works "under the hood", but I imagine that if the field is private then it might have more difficulty (de)serialising it.
public class Program
{
public class A : I
{
public string Word { get; set; }
public int Number { get; set; }
}
public interface I
{
int Number { get; set; }
}
public static void listToJSON(List<I> myList, ref List<string> JSONElements)
{
foreach (var element in myList) {
JSONElements.Add(JsonConvert.SerializeObject(element));
}
}
static void Main(string[] args)
{
List<I> myList = new List<I>();
myList.Add(new A { Word = "hello", Number = 1});
myList.Add(new A { Word = "goodbye", Number = 2});
List<string> jsonElements = new List<string>();
listToJSON(myList, ref jsonElements);
Console.WriteLine(string.Join("\n", jsonElements));
Console.WriteLine("Done, press any key...");
Console.ReadKey();
}
}
I think the reason is best demonstrated by
public void myFunction(List<I> list)
{
list.Add(new B());
}
Let supppose that we have simple class as below:
public class Foo
{
public List<int> l { get; set; }
public Foo(List<int> newList)
{
this.l = newList;
}
}
now we can use it:
List<int> l = new List<int>() { 1, 2 };
Foo foo = new Foo(l);
foreach (int i in foo.l)
Console.WriteLine(i);
Of course, on console we see
1
2
But if we change list l:
l[0] = 11;
l[1] = 22;
and invoke loop again:
foreach (int i in foo.l)
Console.WriteLine(i);
we have on console
11
22
Thus, the list in foo class is changed. Is there any possibility in C#, to see on console again
1
2
so make class Foo such that, the list will be never changed ?
First things first: this is C# and you cannot protect your code against malicious misuse. You can however, make it user-friendly by making it difficult to misuse. For example by using the interface that fulfills all criteria... and not more:
public class Foo
{
public IEnumerable<int> Numbers { get; private set; }
public Foo(IEnumerable<int> numbers)
{
this.Numbers = numbers;
}
}
You can copy the input list, make the setter private and expose an IReadOnlyList<T>:
public class Foo
{
public IReadOnlyList<int> l { get; private set; }
public Foo(IEnumerable<int> newList)
{
this.l = new ReadOnlyCollection<int>(newList.ToList());
}
}
public class Foo
{
private List<int> _l;
public IList<int> L { get { return this._l.AsReadOnly(); } }
public Foo(List<int> newList)
{
this._l = new List<int>(newList);
}
}
You could dress your private variable with a readonly property, or not include the "set" action on your property.
What you actually need is this option #1 or #2
1
private readonly List<int> _l;
public List<int> l { get; set; }
2
public List<int> l { get; }
MSDN readonly prop link
I am trying to figure out how to simplify the following
let's say I have 2 entity classes
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
}
AND
public class B
{
public int Id { get; set; }
public string Nom { get; set; }
public string Ville { get; set; }
}
classes that are similar, but not the same.
each class has a repository classes it uses for CRUD Operations, for example...
public class RepA
{
public static List<A> GetAll()
{
List<A> list = new List<A>();
A a1 = new A() {Id=1, Name="First A", City="Boston"};
A a2 = new A() {Id=2, Name="First B", City="Chicago"};
A a3 = new A() {Id=3, Name="First C", City="San Francisco"};
list.Add(a1);
list.Add(a2);
list.Add(a3);
return list;
}
public static void SaveAll(List<A> list)
{
foreach (A a in list)
{
Console.WriteLine("Saved Id = {0} Name = {1} City={2}",
a.Id, a.Name, a.City);
}
}
}
AND
public class RepB
{
public static List<B> GetAll()
{
List<B> list = new List<B>();
B b1 = new B() {Id=1, Nom="Second A", Ville="Montreal"};
B b2 = new B() {Id=2, Nom="Second B", Ville="Paris"};
B b3 = new B() {Id=3, Nom="Second C", Ville="New Orleans"};
list.Add(b1);
list.Add(b2);
list.Add(b3);
return list;
}
public static void SaveAll(List<B> list)
{
foreach (B b in list)
{
Console.WriteLine("Saved Id = {0} Name = {1} City={2}", b.Id,
b.Nom, b.Ville);
}
}
}
How would I go about making anonymous call to my repository without having to resort to this, because in my real world example, i have 100 repositories, and not 2.
void Main()
{
ChosenType chosentype = RandomChosenType(); //A or B
switch (chosentype)
{
case ChosenType.A:
var listA = RepA.GetAll();
RepA.SaveAll(listA);
break;
case ChosenType.B:
var listB = RepB.GetAll();
RepB.SaveAll(listB);
break;
default:
break;
}
}
Make a base class or use an interface:
public interface IBase<T>
{
List<T> GetAll();
void SaveAll(List<T> items);
}
public class RepA : IBase<RepA>
{
public List<RepA> GetAll() { return new List<RepA>(); }
public void SaveAll(List<RepA> repA) { }
}
public class RepB : IBase<RepB>
{
public List<RepB> GetAll() { return new List<RepB>(); }
public void SaveAll(List<RepB> repB) { }
}
void Main()
{
IBase chosenType = RandomChosenType();
var list = chosenType.GetAll();
}
You should use a single generic repository. The operations should be handled by injected delegates. A repository could look like this:
public class GenericRepositoryExample
{
public void Save<T>(IList<T> persons, SaveDelegate<T> save)
{
foreach (T person in persons)
{
Console.WriteLine(save(person));
}
}
}
Note that the save delegate is passed to the Save method. The SaveDelegate in your example could be declared as:
public delegate string SaveDelegate<T>(T input);
For ease, I have created a HelperClass containing the delegated functions. In real life helper classes should generally be avoided if possible.
public static class HelperClass
{
public static string FrenchSave(B frenchInput)
{
string result = string.Format("ID = {0}; Name = {1}; City = {2}", frenchInput.Id, frenchInput.Nom, frenchInput.ville);
return result;
}
public static string EnglishSave(A englishInput)
{
string result = string.Format("ID = {0}; Name = {1}; City = {2}", englishInput.Id, englishInput.name, englishInput.city);
return result;
}
}
To illustrate the use of this setup, I have created the following unit test:
[Test]
public void TestGenericRepository()
{
IList<A> aList = new List<A>();
aList.Add(new A() { Id = 1, name = "George", city = "Chicago"});
aList.Add(new A() { Id = 2, name = "Bill", city = "Toledo" });
List<B> bList = new List<B>();
bList.Add(new B() {Id= 1, Nom = "Nathalie", ville = "Paris"});
bList.Add(new B() {Id = 2, Nom = "Michelle", ville = "Lyon"});
GenericRepositoryExample repository = new GenericRepositoryExample();
repository.Save<A>(aList,HelperClass.EnglishSave);
repository.Save<B>(bList,HelperClass.FrenchSave);
}
You can make your repositories implement an interface, say IGetAllSaveAll. Then you can store your repositories in a list, and cast them to that interface. That way you'll be able to call the GetAll function on all of them:
(actually the first interface isn't mandatory, you could directly write it as IEnumerable<object> GetAll()...)
interface IGetAllSaveAll<T>
{
IEnumerable<T> GetAll();
void SaveAll(IEnumerable<T> obj);
}
you'll need to have a base interface:
interface IGetAllSaveAll : IGetAllSaveAll<object>
and to use it:
public class RepA: IGetAllSaveAll
public class RepB: IGetAllSaveAll
....
Then you can keep a dictionnary of all these repositories somewhere:
Dictionnary<Type, IGetAllSaveAll> myDic;
Of course you'll still have to add your repositories to your dictionnary:
myDic.Add(typeof(A), new RepA());
And then to call it:
Type t = RandomChosenType();
myDic[t].GetAll();
The code you posted uses static methods. In order to implement an interface, you will need instance methods. Unless you want to use reflection (should be avoided in my opinion), these methods need to be ignorant of the type. Something like this:
public interface IRepository {
IEnumerable<object> GetAll();
}
And in RepA:
IEnumerable<object> IRepository.GetAll() {
return RepA.GetAll();
}
Instead of storing types, each of your menu selections can just contain an instance of the appropriate repository class in a field of type IRepository. After calling GetAll on one of the instances, you can later cast the result to the specific type (like List<A>) if necessary.
Try this approach based on reflection and some assumptions about your classes' structures:
static void Main(string[] args)
{
var types = Assembly.GetExecutingAssembly().Modules
.SelectMany(m => m.GetTypes())
.Where(t =>
t.GetMethod("GetAll") != null &&
t.GetMethod("SaveAll") != null &&
t.GetMethod("GetAll").ReturnType.IsGenericType)
.Select(t =>
new
{
RepositoryType = t,
ReturnTypeArgument =
t.GetMethod("GetAll").ReturnType.GenericTypeArguments[0]
}
)
.ToList();
(new List<dynamic> { new A(), new B() }).ToList().ForEach(chosenType =>
{
var association = types
.FirstOrDefault(t =>
t.ReturnTypeArgument == chosenType.GetType());
if (association == null)
return;
var repType = association.RepositoryType;
dynamic list = repType.GetMethod("GetAll")
.Invoke(chosenType, new object[] { });
repType.GetMethod("SaveAll")
.Invoke(chosenType, new object[] { list });
});
}
Given your exact scenario, where you've got an enum representing each of the possible data types, here's something that may work.
Map each enum value to a repository type using an attribute. Each repository inherits from a generic class, which implements a basic interface which is not strongly typed. The repo methods change from static to instance members. The base repo class has to do casting to cast object to the appropriate type and back, but the actual repository implementations are strongly typed.
You can take this a step further and try to cache some of the reflection using expression trees so you only have to do it once, but it depends on how optimized you really need to make it.
public enum ChosenType {
[Repo(typeof(RepA))] A = 0,
[Repo(typeof(RepB))] B = 1
}
public class RepoAttribute : Attribute {
public RepoAttribute(Type repoType) { RepoType = repoType; }
public Type RepoType { get; set; }
}
class Program
{
static void Main()
{
ChosenType chosentype = RandomChosenType(); //A or B
// Make an instance of the appropriate repo based on the mapping
// to the enum value.
// This is a moderately expensive call, and there's room for improvement
// by using expression trees and caching lambda expressions.
var repo = (IRepo)Activator.CreateInstance(
((RepoAttribute)typeof(ChosenType).GetMember(chosentype.ToString())
.Single().GetCustomAttributes(typeof(RepoAttribute), false).Single()
).RepoType);
var list = repo.GetAll();
repo.SaveAll(list);
Console.Read();
}
static Random _rand = new Random();
static ChosenType RandomChosenType()
{
return (ChosenType)_rand.Next(0, 2);
}
}
public class A { /* No change */ }
public class B { /* No change */ }
public interface IRepo {
List<object> GetAll();
void SaveAll(List<object> list);
}
public abstract class Repo<T> : IRepo {
List<object> IRepo.GetAll() {
return GetAll().Cast<object>().ToList();
}
void IRepo.SaveAll(List<object> list) {
SaveAll(list.Cast<T>().ToList());
}
public abstract List<T> GetAll();
public abstract void SaveAll(List<T> list);
}
public class RepA : Repo<A> {
public override List<A> GetAll() { /* No change except the signature */ }
public override void SaveAll(List<A> list) { /* No change except the signature */ }
}
public class RepB : Repo<B> {
public override List<B> GetAll() { /* No change except the signature */ }
public override void SaveAll(List<B> list) { /* No change except the signature */ }
}