Let's use this simple example :
Connect4Board.cs :
public class Connect4Board
{
private Box[,] _boxes = new Box[7, 6];
public void DropPieceAt(int column, bool redPiece)
{
//Safe modifications to box colors.
}
public Box GetBoxAt(int x, int y)
{
return _boxes[x, y];
}
}
Box.cs :
public class Box
{
public bool IsRed { get; private set; }
public bool IsEmpty { get; private set; }
}
I want GetBoxAt() to return a box with read-only properties. However I want my Connect4Board to be able to change boxes colors.
Let's assume that I don't want to use internal modifier at all.
My solution (quite ugly) :
public class Connect4Board
{
private Box.MutableBox[,] _mutableBoxes = new Box.MutableBox[7, 6];
public Connect4Board()
{
for (int y = 0; y < 6; y++)
{
for (int x = 0; x < 7; x++)
{
_mutableBoxes[x, y] = new Box.MutableBox();
}
}
}
public void DropPieceAt(int column, bool isRed)
{
//Safe modifications to box colors.
}
public Box GetBoxAt(int x, int y)
{
return _mutableBoxes[x, y].Box;
}
}
public class Box
{
public bool IsRed { get; private set; }
public bool IsEmpty { get; private set; }
private Box()
{
}
public class MutableBox
{
public Box Box { get; private set; }
public MutableBox()
{
Box = new Box();
}
public void MakeRed() { //I can modify Box here }
public void MakeYellow() { //I can modify Box here }
public void MakeEmpty() { //I can modify Box here }
}
}
Is there a good design pattern to make this more elegant ?
There are a number of strategies you could use.
It's often useful to program to interfaces. The IBox interface below would not allow people to edit the box (without casting it to a Box), but still leaves your code simple.
public class Connect4Board
{
private Box[,] _boxes = new Box[7, 6];
public void DropPieceAt(int column, bool redPiece)
{
//Safe modifications to box colors.
}
public IBox GetBoxAt(int x, int y)
{
return _boxes[x, y];
}
}
public interface IBox
{
bool IsRed { get; }
bool IsEmpty { get; }
}
public class Box : IBox
{
public bool IsRed { get; set; }
public bool IsEmpty { get; set; }
}
Another approach would be to make boxes always immutable (like strings), and instead of modifying the states of your boxes, you just modify which box is in which location in your array:
public class Connect4Board
{
private Box[,] _boxes = new Box[7, 6];
public Connect4Board()
{
for(int i = 0; i<7; i++)
{
for(int j = 0; j<6; j++)
{
// Notice how you're not changing a color, but assigning the location
_boxes[i,j] = Box.Empty;
}
}
}
public void DropPieceAt(int column, bool redPiece)
{
// Modifications to the top empty location in the given column.
}
public Box GetBoxAt(int x, int y)
{
return _boxes[x, y];
}
}
public class Box
{
public bool IsRed { get; private set; }
public bool IsBlack { get; private set; }
public bool IsEmpty { get; private set; }
private Box() {}
public static readonly Box Red = new Box{IsRed = true};
public static readonly Box Black = new Box{IsBlack = true};
public static readonly Box Empty = new Box{IsEmpty = true};
}
WOuld this work for you?
Make Box Immutable, with static factory, and add static properties that return new boxes with various colors
public class Box
{
private Box() {}
private Box(Color color) { Color = color; }
public static Box Make(Color color) { return new Box(color); }
public static Box RedBox { get { return new Box(Color.Red); } }
public static Box GreenBox { get { return new Box(Color.Green); } }
public static Box BlueBox { get { return new Box(Color.Blue); } }
// ... etc.
}
Solution 1
You could create a wrapper around Box which is immutable. Connect4Board would use the MutableBox class internally but would expose ImmutableBox to consumers.
public interface IBox
{
bool IsRed { get; }
bool IsEmpty { get; }
}
public class MutableBox : IBox
{
public bool IsRed { get; set; }
public bool IsEmpty {get; set; }
public IBox MakeImmutable()
{
return new ImmutableBox(this);
}
}
public class ImmutableBox : IBox
{
private IBox innerBox;
public ImmutableBox(IBox innerBox) { this.innerBox = innerBox; }
public bool IsRed { get { return innerBox.IsRed; } }
public bool IsEmpty { get { return innerBox.IsEmpty; } }
}
public class Connect4Board
{
private MutableBox[,] boxes = new MutableBox[7, 6];
public void DropPieceAt(int column, bool redPiece)
{
// perform modifications
}
public IBox GetBoxAt(int x, int y)
{
return boxes[x,y].MakeImmutable();
}
}
Solution 2
You could maybe use explicit interface implementation to achieve this?
Create an interface IMutableBox.
public interface IMutableBox
{
void SetIsRed(bool isRed);
void SetIsEmpty(bool isEmpty);
}
public class Box : IMutableBox
{
private bool isRed;
private bool isEmpty;
public bool IsRed { get { return isRed; } }
public bool IsEmpty { get { return isEmpty; } }
void IMutableBox.SetIsRed(bool isRed)
{
this.isRed = isRed;
}
void IMutableBox.SetIsEmpty(bool isEmpty)
{
this.isEmpty = isEmpty;
}
}
Now, in order to mutate Box, you would need to cast it to an IMutableBox.
var box = new Box();
var mutableBox = box as IMutableBox;
mutableBox.SetEmpty(true);
You can make a ReadOnlyBox that can be a facade for your Box much like a ReadOnlyCollection.
[Flags]
public enum BoxState
{
Empty = 0,
Red = 1 << 0,
Black = 1 << 1
}
[Flags]
public enum BoardColor
{
Red = 1 << 0,
Black = 1 << 1
}
public interface IBox
{
BoxState State { get; }
}
public class Box : IBox
{
public BoxState State { get; set; }
}
public class ReadOnlyBox : IBox
{
private readonly IBox _box;
public ReadOnlyBox(IBox box)
{
_box = box;
}
public BoxState State { get { return _box.State; } }
}
public class Connect4Board
{
private const int _boardWidth = 7;
private const int _boardHeight = 6;
private Box[,] _boxes = new Box[_boardWidth, _boardHeight];
public void DropPieceAt(int column, BoardColor color)
{
for(int height = 0; height < _boardHeight; height++)
{
if(_boxes[column, height].State != BoxState.Empty) continue;
_boxes[column, height].State = (BoxState)color;
break;
}
}
public IBox GetBoxAt(int x, int y)
{
return new ReadOnlyBox(_boxes[x, y]);
}
}
Related
I have 3 interfaces.
public interface IItem
{
string Name { get; set; }
}
public interface IEquipable : IItem
{
void Equip();
}
public interface IConsumable : IItem
{
void Use();
}
IEquipable is implemented by the classes Helmet and Bow, and IConsumable is implemented by classes Potion and Food.
Then, I have a class with a property which contains a List of IItem, and proceed to add a few items of both IEquipable and IConsumable after instantiating it.
public class Character
{
public List<IItem> Items { get; private set; }
public Character()
{
this.Items = new List<IItem>();
}
public void AddItem(IItem item)
{
this.Items.Add(item);
}
}
Program.cs
...
Character char = new Character();
char.AddItem(new Potion());
char.AddItem(new Food());
char.AddItem(new Helmet());
char.AddItem(new Bow());
...
Is there a way I can get a List of all IEquipable members from the List of IItems, each AS IEquipable?
I want to do something like
...
List<IEquipable> equipmentList = //do something to char.Items and get all items of type IEquipable.
IEquipment equipment = equipmentList.First(...)
equipment.Equip();
...
I've tried using List<IEquipable> equipmentList = char.Items.OfType<IEquipable>().ToList() but the resulting list ends up empty.
I implemented (and fixed minor typos in) your code like this:
void Main()
{
Character character = new Character();
character.AddItem(new Potion());
character.AddItem(new Food());
character.AddItem(new Helmet());
character.AddItem(new Bow());
List<IEquipable> equipmentList = character.Items.OfType<IEquipable>().ToList();
}
public class Potion : IConsumable
{
public string Name { get; set; }
public void Use()
{
throw new NotImplementedException();
}
}
public class Food : IConsumable
{
public string Name { get; set; }
public void Use()
{
throw new NotImplementedException();
}
}
public class Helmet : IEquipable
{
public string Name { get; set; }
public void Equip()
{
throw new NotImplementedException();
}
}
public class Bow : IEquipable
{
public string Name { get; set; }
public void Equip()
{
throw new NotImplementedException();
}
}
public interface IItem
{
string Name { get; set; }
}
public interface IEquipable : IItem
{
void Equip();
}
public interface IConsumable : IItem
{
void Use();
}
public class Character
{
public List<IItem> Items { get; private set; }
public Character()
{
this.Items = new List<IItem>();
}
public void AddItem(IItem item)
{
this.Items.Add(item);
}
}
Your exact code (albeit char renamed to character) works perfectly fine. The equipmentList ends up with two elements. The issue you're seeing, i.e. "the resulting list ends up empty", is not reproducible with the code you've posted.
You can use the OfType method
Filters the elements of an IEnumerable based on a specified type.
Signature
public static IEnumerable<TResult> OfType<TResult> (this IEnumerable source)
Usage
var equipable = Character.Items.OfType<IEquipable>();
Or encapsulate it as a method in the instance or an extension method if you like
So it does work like I wanted. My actual code just had another issue and I'm a dummy for not actually posting that. So here it is, for future reference.
using System.Collections.Generic;
using RolePlayGame.Library.Items.Backstage;
using System.Linq;
using System.Text;
using System;
namespace RolePlayGame.Library.Characters.Backstage
{
public class Inventory
{
public List<IItem> StoredItems { get; private set; }
public List<EquippedItem> Gear { get; private set; }
public Inventory()
{
this.StoredItems = new List<IItem>();
this.Gear = new List<EquippedItem>();
}
public bool HasItem(string name)
{
return this.StoredItems.Exists(item => item.Name == name);
}
public bool HasItem(IItem item)
{
return this.StoredItems.Contains(item);
}
public void RemoveItem(string name)
{
int firstIndex = this.StoredItems.FindIndex(item => item.Name == name);
if (firstIndex != -1)
{
this.StoredItems.RemoveAt(firstIndex);
}
}
public void RemoveItem(IItem item)
{
int firstIndex = this.StoredItems.IndexOf(item);
if (firstIndex != -1)
{
this.StoredItems.RemoveAt(firstIndex);
}
}
public void AddItem(IItem item, int quantity)
{
for (int i = 0; i < quantity; i++)
{
this.StoredItems.Add(item);
}
}
public void AddItem(IItem item)
{
this.StoredItems.Add(item);
}
public bool CheckEquipmentSlot(EquipmentSlot slot)
{
return this.Gear.Exists(item => item.UsedSlots.Contains(slot));
}
public bool HasEquipment(IEquipment equipment)
{
return this.Gear.Exists(item => item.Item == equipment);
}
public void AddEquipment(IEquipment equipment)
{
IEquipment alreadyEquipped;
foreach (EquipmentSlot slot in equipment.SlotsUsed)
{
if (this.Gear.Exists(item => item.UsedSlots.Contains(slot)))
{
alreadyEquipped = this.Gear.Find(item => item.UsedSlots.Contains(slot)).Item;
this.RemoveEquipment(slot);
this.StoredItems.Add(alreadyEquipped);
}
}
EquippedItem newEquipment = new EquippedItem(equipment);
this.Gear.Add(newEquipment);
}
public void RemoveEquipment(EquipmentSlot slot)
{
this.Gear.RemoveAll(equipment => equipment.UsedSlots.Contains(slot));
}
public int GetAttributeBonusTotal(AttributeType attribute)
{
int bonusTotal = 0;
foreach (IEquipment item in this.StoredItems.OfType<IEquipment>().ToList())
{
bonusTotal += item.GetAttributeBonus(attribute);
}
return bonusTotal;
}
public int GetCarryWeight()
{
int totalWeight = 0;
foreach (IItem item in StoredItems)
{
totalWeight += item.Weight;
}
return totalWeight;
}
public string GearToString()
{
StringBuilder builder = new StringBuilder();
builder.Append(" Equipped Gear:");
foreach (EquippedItem equipment in this.Gear)
{
builder.Append($"\n {equipment.Item.Name}");
}
return builder.ToString();
}
public string ItemsToString()
{
StringBuilder builder = new StringBuilder();
builder.Append(" Inventory:");
foreach (IItem item in this.StoredItems.Distinct())
{
builder.Append($"\n {item.Name} x {this.StoredItems.FindAll(value => value == item).Count()}");
}
return builder.ToString();
}
public int GetDefenseRateAgainstTypeTotal(DamageType againstType)
{
int rate = 0;
List<IOutfit> outfits = this.Gear.Select(value => value.Item).OfType<IOutfit>().ToList();
foreach (IOutfit item in outfits)
{
rate += item.GetDefenseRateAgainstType(againstType);
}
return rate;
}
}
}
One of the last lines has the problem (now fixed). List<IOutfit> outfits = this.Gear.Select(value => value.Item).OfType<IOutfit>().ToList(); used to be List<IOutfit> outfits = this.Gear.OfType<IOutfit>().ToList();. But Gear is of type List<EquippedItem>, and EquippedItem is not an implementation of IItem.
Here is EquippedItem.cs
using RolePlayGame.Library.Items.Backstage;
using System.Collections.Generic;
namespace RolePlayGame.Library
{
public class EquippedItem
{
public List<EquipmentSlot> UsedSlots { get; set; }
public IEquipment Item { get; set; }
public EquippedItem(IEquipment equipment)
{
this.Item = equipment;
this.UsedSlots = equipment.SlotsUsed;
}
}
}
I needed to select the Item property from the items inside Gear as another list before doing the type filtering with .OfType<IOutfit>(). That's where .Select(value => value.Item) enters the stage.
So that's that. I'll learn to post actual code for future questions.
How can I migrate a configuration to a new class from a list of bools? Previously it used a list of bool, but the list was being abused as a class, with each index having a specific meaning like a field.
I want to migrate it from a List, to a class that instead acts as the list for serialization purposes, but exposes normal fields to the rest of the application.
How can I write class ListEmulator so that it serializes out to a list, without introducing new xml tags?
old code
namespace
{
[DataContract]
public class Configuration
{
public const string FileName = "Configuration.xml";
public Configuration()
{
AList = new List<bool>();
AGuidList = new List<Guid>();
}
[DataMember]
public List<Guid> AGuidList { get; set; }
[DataMember]
public List<bool> AList { get; set; }
}
}
new code.
namespace
{
[DataContract]
public class Configuration
{
public const string FileName = "Configuration.xml";
public Configuration()
{
AListEmulator = new ListEmulator();
AGuidList = new List<Guid>();
}
[DataMember]
public List<Guid> AGuidList { get; set; }
[DataMember]
public ListEmulator AListEmulator { get; set; }
}
}
public class ListEmulator
{
public ListEmulator()
{
new ListEmulator(true, true, true, true);
}
public ListEmulator(bool item0, bool item1, bool item2, bool item3)
{
this.IsPlanned = item0;
this.IsCompleted = item1;
this.IsRemaining = item2;
this.IsSerial = item3;
}
public bool IsPlanned { get; set; }
public bool IsCompleted { get; set; }
public bool IsRemaining { get; set; }
public bool IsSerial { get; set; }
}
The reason a list is needed, is there is old migration code that needs to be ported for when there was only 1 element, then 2, 3, 4 with different defaults for each. If it weren't for the fact that I have existing deployed configuration files in the original format, it would probably be time for them to all be named in the XML individually. However, I need to retain the current format for now. For the sake of migration I'm wondering how I can accomplish the above.
One option would be for your ListEmulator to inherit from Collection<bool>, and then add specific named properties to access elements in the array, like so:
public class ListEmulator : Collection<bool>
{
const bool IsPlannedDefault = false; // Change to the appropriate values.
const bool IsCompletedDefault = false;
const bool IsRemainingDefault = false;
const bool IsSerialDefault = false;
void AddAllDefaults()
{
// Customize the code here to upgrade old collections with fewer than 4 elements to the current 4-element format.
if (Count < 1)
Add(IsPlannedDefault);
if (Count < 2)
Add(IsCompletedDefault);
if (Count < 3)
Add(IsRemainingDefault);
if (Count < 4)
Add(IsSerialDefault);
}
public ListEmulator() { }
public ListEmulator(bool item0, bool item1, bool item2, bool item3)
{
this.IsPlanned = item0;
this.IsCompleted = item1;
this.IsRemaining = item2;
this.IsSerial = item3;
}
public bool IsPlanned { get { return this.ElementAtOrDefault(0, IsPlannedDefault); } set { AddAllDefaults(); this[0] = value; } }
public bool IsCompleted { get { return this.ElementAtOrDefault(1, IsCompletedDefault); } set { AddAllDefaults(); this[1] = value; } }
public bool IsRemaining { get { return this.ElementAtOrDefault(2, IsRemainingDefault); } set { AddAllDefaults(); this[2] = value; } }
public bool IsSerial { get { return this.ElementAtOrDefault(3, IsSerialDefault); } set { AddAllDefaults(); this[3] = value; } }
protected override void InsertItem(int index, bool item)
{
if (index > 3)
throw new ArgumentOutOfRangeException("index > 3");
base.InsertItem(index, item);
}
}
Then in your Configuration simply replace List<bool> with ListEmulator, keeping the old element name:
[DataMember]
public ListEmulator AList { get; set; }
Because this type implements IEnumerable<T>, the DataContractSerializer will serialize it as a collection rather than as an object with properties. (You might want to change the class name since it's really not a list emulator at this point.) However, this only works if you do not add any initial values to the collection from within the default constructor.
Another option would be to add a surrogate property to Configuration that handles the necessary conversions, and mark the ListEmulator AList as not serialized:
[DataContract]
public class Configuration
{
public const string FileName = "Configuration.xml";
public Configuration()
{
AList = new ListEmulator();
AGuidList = new List<Guid>();
}
[DataMember]
public List<Guid> AGuidList { get; set; }
[DataMember(Name = "AList")]
bool[] AlistArray
{
get
{
return AList == null ? null : AList.ToArray();
}
set
{
AList = new ListEmulator(value);
}
}
[IgnoreDataMember] // Do not serialize this property directly
public ListEmulator AList { get; set; }
}
public class ListEmulator
{
const bool IsPlannedDefault = false; // Change to the appropriate values.
const bool IsCompletedDefault = false;
const bool IsRemainingDefault = false;
const bool IsSerialDefault = false;
public ListEmulator(IList<bool> list)
{
IsPlanned = list.ElementAtOrDefault(0, IsPlannedDefault);
IsCompleted = list.ElementAtOrDefault(1, IsCompletedDefault);
IsRemaining = list.ElementAtOrDefault(2, IsRemainingDefault);
IsSerial = list.ElementAtOrDefault(3, IsSerialDefault);
}
public ListEmulator()
{
new ListEmulator(true, true, true, true);
}
public ListEmulator(bool item0, bool item1, bool item2, bool item3)
{
this.IsPlanned = item0;
this.IsCompleted = item1;
this.IsRemaining = item2;
this.IsSerial = item3;
}
public bool IsPlanned { get; set; }
public bool IsCompleted { get; set; }
public bool IsRemaining { get; set; }
public bool IsSerial { get; set; }
public bool[] ToArray()
{
return new[] { IsPlanned, IsCompleted, IsRemaining, IsSerial };
}
}
Both options use the following extension method:
public static class ListExtensions
{
public static T ElementAtOrDefault<T>(this IList<T> list, int index, T defaultValue)
{
if (index < 0)
throw new ArgumentOutOfRangeException(string.Format("index = {0}", index));
if (list == null || index >= list.Count)
return defaultValue;
return list[index];
}
}
Create three small classes unrelated by inheritance—classes Building, Car and Bicycle. Write an interface ICarbonFootprint with a GetCarbonFootprint method. Have each of your classes implement that interface, so that its GetCarbonFootprint method calculates an appropriate carbon footprint for that class (check out a few websites that explain how to calculate carbon footprints). Write an app that creates objects of each of the three classes, places references to those objects in List, then iterates through the List, polymorphically invoking each object’s GetCarbonFootprint method. Constructor of Car initialize “gallon of gas”, and the Building constructor will initialize buiding-square-footage.
how to calculate carbon-footprint
One gallon of gas yields 20 pounds of CO2 for a car
Multiply the square footage by 50 for a building
None for a bicycle
My instructor's code:
public static void Main(string[] args)
{
ICarbonFootprint[] list = new ICarbonFootprint[3];
// add elements to list
list[0] = new Bicycle();
list[1] = new Building(2500);
list[2] = new Car(10);
// display carbon footprint of each object
for (int i = 0; i < list.Length; i++)
list[i].GetCarbonFootprint();
} // end Main
}
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Miller
{
class Program
{
static void Main(string[] args)
{
Bicycle bike = new Bicycle();
Building b = new Building();
Car car = new Car();
List<ICarbonFootprint> list = new List<ICarbonFootprint>();
list.Add(bike);
list.Add(b);
list.Add(car);
int totalCarbon = 0;
foreach (var item in list)
{
totalCarbon += item.GetCarbonFootprint();
Console.WriteLine("{0} has a footprint of: {1}", item, item.GetCarbonFootprint());
}
Console.WriteLine("Total footprint is: {0}", totalCarbon);
Console.ReadKey();
}
}
public class Bicycle : ICarbonFootprint
{
private string _make;
private string _model;
public string Make
{
get { return _make; }
set { _make = value; }
}
public string Model
{
get { return _model; }
set { _model = value; }
}
public int GetCarbonFootprint()
{
return 10;
}
public override string ToString()
{
return string.Format("Bike");
}
}
public class Building : ICarbonFootprint
{
private string _address;
public string Address
{
get { return _address; }
set { _address = value; }
}
public int GetCarbonFootprint()
{
return 2000;
}
public override string ToString()
{
return string.Format("Building");
}
}
public class Car : ICarbonFootprint
{
private string _make;
private string _model;
public string Make
{
get { return _make; }
set { _make = value; }
}
public string Model
{
get { return _model; }
set { _model = value; }
}
public int GetCarbonFootprint()
{
return 1500;
}
public override string ToString()
{
return string.Format("Car");
}
}
public interface ICarbonFootprint
{
int GetCarbonFootprint();
}
}
Me integrating my instructor's code (lines 12-23 changed AKA class Program was the only thing changed):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Miller
{
class Program
{
public static void Main(string[] args)
{
ICarbonFootprint[] list = new ICarbonFootprint[3];
// add elements to list
list[0] = new Bicycle();
list[1] = new Building(2500);
list[2] = new Car(10);
// display carbon footprint of each object
for (int i = 0; i < list.Length; i++)
list[i].GetCarbonFootprint();
} // end Main
}
public class Bicycle : ICarbonFootprint
{
private string _make;
private string _model;
public string Make
{
get { return _make; }
set { _make = value; }
}
public string Model
{
get { return _model; }
set { _model = value; }
}
public int GetCarbonFootprint()
{
return 10;
}
public override string ToString()
{
return string.Format("Bike");
}
}
public class Building : ICarbonFootprint
{
private string _address;
public string Address
{
get { return _address; }
set { _address = value; }
}
public int GetCarbonFootprint()
{
return 2000;
}
public override string ToString()
{
return string.Format("Building");
}
}
public class Car : ICarbonFootprint
{
private string _make;
private string _model;
public string Make
{
get { return _make; }
set { _make = value; }
}
public string Model
{
get { return _model; }
set { _model = value; }
}
public int GetCarbonFootprint()
{
return 1500;
}
public override string ToString()
{
return string.Format("Car");
}
}
public interface ICarbonFootprint
{
int GetCarbonFootprint();
}
}
So, replacing my code for class Program with my instructor's code, I received the following errors:
Program.cs(51,23,51,41): error CS1729: 'Miller.Building' does not contain a constructor that takes 1 arguments
Program.cs(52,23,52,34): error CS1729: 'Miller.Car' does not contain a constructor that takes 1 arguments
Now, because the last two days before Spring break were cancelled due to the weather (snow), we weren't able to discuss. My code seems to do what the directions ask, but I would like to get my instructor's code for class Program working with my code. Could someone help me with these errors possibly?
There are a few issues with your code.
First up you need to include the constructors to make the code compile.
For Building this would look like:
private int squareFootage;
public Building(int squareFootage)
{
this.squareFootage = squareFootage;
}
And for Car this would look like:
private int gasGallons;
public Car(int gasGallons)
{
this.gasGallons = gasGallons;
}
Next, you're not following the rules for calculating the carbon footprint.
They should be:
//Bicycle
public int GetCarbonFootprint()
{
return 0;
}
//Building
public int GetCarbonFootprint()
{
return 50 * squareFootage;
}
//Car
public int GetCarbonFootprint()
{
return 20 * gasGallons;
}
Finally, your instructor's code doesn't actually display any results. The code in the for loop should be changed to be Console.WriteLine(list[i].GetCarbonFootprint()); if this is a console app.
So, all up the code should look like this:
public static void Main(string[] args)
{
ICarbonFootprint[] list = new ICarbonFootprint[3];
// add elements to list
list[0] = new Bicycle();
list[1] = new Building(2500);
list[2] = new Car(10);
// display carbon footprint of each object
for (int i = 0; i < list.Length; i++)
Console.WriteLine(list[i].GetCarbonFootprint());
}
public class Bicycle : ICarbonFootprint
{
public string Make { get; set; }
public string Model { get; set; }
public int GetCarbonFootprint()
{
return 0;
}
}
public class Building : ICarbonFootprint
{
private int squareFootage;
public Building(int squareFootage)
{
this.squareFootage = squareFootage;
}
public string Address { get; set; }
public int GetCarbonFootprint()
{
return 50 * squareFootage;
}
}
public class Car : ICarbonFootprint
{
private int gasGallons;
public Car(int gasGallons)
{
this.gasGallons = gasGallons;
}
public string Make { get; set; }
public string Model { get; set; }
public int GetCarbonFootprint()
{
return 20 * gasGallons;
}
}
public interface ICarbonFootprint
{
int GetCarbonFootprint();
}
I've opted to short-cut the property definitions rather than implement them with fields.
The output is:
0
125000
200
You should write constructors for Building and Car like next:
public Building(int MyValue)
{
...
}
and your code will work fine.
Suggestion: Car and Bicycle shares properties, and the ICarbonFootprint implementation, so you can create a base class with an abstract method. Also the GetCarbonFootprint from ICarbonFootprint interface must be type of System.Double.
public interface ICarbonFootprint
{
int GetCarbonFootprint();
}
public class Building : ICarbonFootprint
{
public int BuildingSquareFootage { get; set; }
public string Address { get; set; }
public Building(int buildingSquareFootage, string address)
{
BuildingSquareFootage = buildingSquareFootage;
Address = address;
}
public int GetCarbonFootprint()
{
return BuildingSquareFootage * 50;
}
public override string ToString()
{
return string.Format("Building");
}
}
public abstract class CarBicycleBase : ICarbonFootprint
{
public string Make { get; set; }
public string Model { get; set; }
protected CarBicycleBase(string make, string model)
{
Make = make;
Model = model;
}
public abstract int GetCarbonFootprint();
}
public class Bicycle : CarBicycleBase
{
public Bicycle(string make, string model)
: base(make, model) { }
public override int GetCarbonFootprint()
{
return 0;
}
public override string ToString()
{
return string.Format("Bike");
}
}
public class Car : CarBicycleBase
{
public int GallonOfGas { get; set; }
public Car(int gallonOfGas, string make, string model)
: base(make, model)
{
GallonOfGas = gallonOfGas;
}
public override int GetCarbonFootprint()
{
return GallonOfGas * 20;
}
public override string ToString()
{
return string.Format("Car");
}
}
Example:
...
var list = new List<ICarbonFootprint>(3)
{
new Car(10, "...", "..."),
new Bicycle("...", "..."),
new Building(20, "...")
};
foreach (ICarbonFootprint item in list)
item.GetCarbonFootprint();
...
I hope it helps.
I want to implement a command pattern. I have the following:
public class State
{
public int Number { get; set; }
public void Execute(IAction action)
{
if (action.IsValid(this))
action.Apply(this);
}
}
public interface IAction
{
bool IsValid(State state);
void Apply(State state);
}
public class ActionSet5IfZero : IAction
{
public bool IsValid(State state)
{
if (state.Number == 0)
return true;
else
return false;
}
public void Apply(State state)
{
state.Number = 5;
}
}
And the program:
static void Main(string[] args)
{
State s = new State();
s.Execute(new ActionSet5IfZero());
}
That works as expected. My problem begins, when I would like to extend the State class:
public class ExtendedState : State
{
public int Number2 { get; set; }
}
Now the action must apply changes on ExtendedState. So I thought I would create extended action that has two additional functions that take ExtendedState as a parameter:
public class ExtendedActionSet5IfZero : IAction
{
public bool IsValid(State state)
{
throw new NotImplementedException();
}
public void Apply(State state)
{
throw new NotImplementedException();
}
public bool IsValid(ExtendedState state)
{
if (state.Number == 0 && state.Number2 == 0)
return true;
else
return false;
}
public void Apply(ExtendedState state)
{
state.Number = 5;
state.Number2 = 5;
}
}
This is something I already do not like because the functions that implement the interface become redundant. Moreover I need to create a new Execute function in my ExtendedState that utilizes the new type and not IAction (otherwise not implemented functions get called).
I am sure it can be done in a nice OO way. Can you help me out? The aim is to create an extensible State class and IAction interface (maybe even generic, I do not know), so I can extend the State but remain the generic functionality without additional coding.
You could add a virtual SetNumber method to state
public class State
{
public int Number { get; set; }
public virtual void SetNumber(int n)
{
Number = n;
}
public void Execute(IAction action)
{
if (action.IsValid(this))
action.Apply(this);
}
}
In the extended state you orverride it
public class ExtendedState : State {
public int Number2 { get; set; }
public orverride void SetNumber(int n)
{
base.SetNumber(n);
Number2 = n;
}
}
The action would then be implemented like this
public void Apply(State state)
{
state.SetNumber(5);
}
EDIT:
What about declaring Number as array?
public class State
{
public int[] Numbers { get; private set; }
public State()
{
Numbers = new int[1];
}
...
}
The action then does this
public void Apply(State state)
{
for (int i = 0; i < state.Numbers.Length; i++) {
state.Numbers[i] = 5;
}
}
The constructor of ExtendedState would initialize Numbers with
Numbers = new int[2];
In addition, you could have properties for the single numbers
public int Number {
get { return Numbers[0]; }
set { Numbers[0] = value; }
}
and
public int Number2 {
get { return Numbers[1]; }
set { Numbers[1] = value; }
}
You could use generics:
interface IAction<TState> where TState: State
{
bool IsValid(TState state);
void Apply(TState state);
}
How about adding StateContainer to State and Action:
public interface IStateContainer<TState, TAction> where TState : IState where TAction : IAction<TState> {
public TState State;
public void Execute(TAction action);
}
public interface IState { }
public interface IAction<TState> where TState : IState {
bool IsValid(TState state);
void Apply(TState state);
}
Then your original classes can be replaced with:
public class ValidatingStateContainer<TState, TAction> : IStateContainer<TState, TAction> {
public ValidatingStateContainer(TState state) {
State = state;
}
public TState State { get; private set; }
public void Execute(TAction action)
{
if (action.IsValid(this))
action.Apply(State);
}
}
public class ActionSet5IfZero : IAction<NumberState>
{
public boolean IsValid(NumberState state)
{
if (state.Number == 0)
return true;
else
return false;
}
public void Apply(NumberState state)
{
state.Number = 5;
}
}
public class ExtendedActionSet5IfZero : ActionSet5IfZero, IAction<TwoNumberState>
{
public boolean IsValid(TwoNumberState state)
{
if (base.IsValid(state) && state.Number2 == 0)
return true;
else
return false;
}
public void Apply(TwoNumberState state)
{
base.Apply(state);
state.Number2 = 5;
}
}
public class NumberState : IState {
public int Number { get; set; }
}
public class TwoNumberState : NumberState {
public int Number2 { get; set; }
}
I'm trying to sort a custom BindingList. But i came across the problem that my Comparer does not recognize the properties of my class.
The "x.Code_PK_OriginalValue" is not recognized. The weird thing is that intellisense marks "Begrenzingen" in Comparer class different as "Begrenzingen" in the first code block beneath.
BindingListX<Begrenzingen> lst = new BindingListX<Begr.....;
lst.OrderBy(t => t, new CustomComparer<Begrenzingen>());
.
public class CustomComparer<Begrenzingen> : IComparer<Begrenzingen>
{
private readonly Comparison<Begrenzingen> _comparison;
public CustomComparer()
{
_comparison = new Comparison<Begrenzingen>(
(Begrenzingen x, Begrenzingen y) =>
{
return x.Code_PK_OriginalValue.CompareTo(y.Code_PK_OriginalValue);
}
);
}
public int Compare(Begrenzingen x, Begrenzingen y)
{
return _comparison(x, y);
}
}
.
public class BindingListX<T> : BindingList<T>
{
public void OrderBy(Func<T,T> keySelector, IComparer<T> comparer)
{
this.Items.OrderBy(keySelector, comparer);
}
}
.
public class Begrenzingen : DefaultTable, IComparable<Begrenzingen>
{
public Begrenzingen()
{ //New -> Insert DB
Code_PK_OriginalValue = -1;
isDeleted = false;
}
public decimal Code_PK_OriginalValue { get; set; }
public decimal Code_PK { get; set; }
public string Naam { get; set; }
public decimal? SeqLayer { get; set; }
public Boolean isDeleted { get; set; }
public string SeqLayerDisplayValue {
get {
if (SeqLayer == null) return string.Empty;
return (from sdo in MainWindow.Main.SdoLayers where sdo.SeqLayer == this.SeqLayer select sdo.DisplayValue).First();
}
}
public override string ToString()
{
return String.Format("{0};{1};{2};{3}", Code_PK_OriginalValue, Code_PK, Naam, SeqLayer);
}
public int CompareTo(Begrenzingen o)
{
return Code_PK.CompareTo(o.Code_PK);
}
}
How about just lst.OrderBy(t => t.Code_PK_OriginalValue);