I am trying to refactor some code and have to admit that I am new to the concept of generics.
I have a base class:
BaseVarDef.cs:
using UnityEngine;
public abstract class BaseVarDef<T> : ScriptableObject
{
[Multiline]
public string DeveloperDescription = "";
public T Value;
public void SetValue(T value)
{
Value = value;
}
}
Then I have several classes that derive from this. But they all contain the same 3 methods that I would like to refactor into this generic base class. The only difference is they expect either the generic type or class that is currently defining them.
FloatVar.cs
using UnityEngine;
[CreateAssetMenu(fileName = "New FloatVar", menuName = "Variables/FloatVar")]
public class FloatVar : BaseVarDef<float>
{
public void SetValue(FloatVar value)
{
Value = value.Value;
}
public void ApplyChange(float amount)
{
Value += amount;
}
public void ApplyChange(FloatVar amount)
{
Value += amount.Value;
}
}
StringVar.cs
using UnityEngine;
[CreateAssetMenu(fileName = "New StringVar", menuName = "Variables/StringVar")]
public class StringVar : BaseVarDef<string>
{
public void SetValue(StringVar value)
{
Value = value.Value;
}
public void ApplyChange(string amount)
{
Value += amount;
}
public void ApplyChange(StringVar amount)
{
Value += amount.Value;
}
}
Is there a way to refactor the: SetValue and the 2 overloaded ApplyChange methods into BaseVarDef.cs?
Thanks in advance
I think you can define all Apply and Set in Base class
public abstract class BaseVarDef<T>
{
public string DeveloperDescription = "";
public T Value;
public void SetValue(T value)
{
Value = value;
}
public void SetValue(BaseVarDef<T> value)
{
Value = value.Value;
}
public void ApplyChange(T amount)
{
AddValue(amount);
}
public void ApplyChange(BaseVarDef<T> amount)
{
AddValue(amount.Value);
}
protected abstract void AddValue(T val);
}
public class FloatVar : BaseVarDef<float>
{
protected override void AddValue(float val)
{
Value += val;
}
}
If you can just add those methods to the original base class (and then remove the abstract from it), and then you just do BaseVarDef<string> or BaseVarDef<float>. No more need for the FloatVar or StringVar classes!
The tricky part here is the += operator, which can't be applied to generics (at least there is no constraint I'm aware of that allows you to specify the type implements an operator).
So you could either make an abstract Add method that derived classes must implement as Svetlana describes, or you can use dynamic as shown below (and get rid of the need for derived classes altogether).
The one problem with dynamic is that there is no compile time checking with dynamic types, so it's not foolproof. The type T must overload the + operator or you'll get a runtime exception. You could avoid that by wrapping the line Value = amt + val; in a try/catch, but then you may get unexpected behavior.
public class BaseVarDef<T>
{
public string DeveloperDescription { get; set; } = "";
public T Value { get; private set; }
public void SetValue(T value)
{
Value = value;
}
public void SetValue(BaseVarDef<T> value)
{
Value = value.Value;
}
public void ApplyChange(T amount)
{
AddToValue(amount);
}
public void ApplyChange(BaseVarDef<T> amount)
{
AddToValue(amount.Value);
}
private void AddToValue(T amount)
{
dynamic amt = amount;
dynamic val = Value;
// Empty catch to avoid runtime exception if 'T' doesn't support the '+' operator
try { Value = amt + val; }
catch { }
}
}
Related
I have abstract base class that contains some fields and some methods that act on these fields. For example:
public abstract class A
{
protected double _field;
public double SquaredField { get { return _field * _field; } }
... some other abstract methods
}
I want to impose that all children of A initialize _field in their constructors
public class B : A
{
public B(double field)
{
_field = Math.Sqrt(field);
}
... some other method implementations
}
What's the correct pattern to achieve this?
-- EDIT
What I ended up doing is:
public abstract class A
{
protected readonly double _field;
public A(double field)
{
_field = field;
}
public double SquaredField { get { return _field * _field; } }
... some other abstract methods
}
public class B : A
{
public B(double field) : base(field)
{
}
public static B CreateNew(double val)
{
return new B(Math.Sqrt(field));
}
... some other method implementations
}
Don't expose a field to the derived classes at all. Instead, create a protected abstract property:
public abstract class A
{
protected double Field { get; }
public double SquaredField { get { return Field * Field; } }
}
Or, if the field should always be constant for a particular instance, make it a constructor parameter and keep it private:
public abstract class A
{
private readonly double _field;
public double SquaredField { get { return _field * _field; } }
protected A(double field)
{
_field = field;
}
}
Don't let class A have a parameterless constructor:
public abstract class A
{
protected double _field;
public double SquaredField { get { return _field * _field; } }
// Require any fields that must be initialized in the base class's
// constructor. If there are a lot of such fields, consider encapsulating
// them all in their own class, e.g. AArgs.
protected A(double field)
{
_field = field;
}
}
public class B : A
{
// You must call a base class constructor as below, because class A
// no longer has a parameterless constructor to use by default.
public B(double field)
: base(field)
{
}
}
Addendum
If you can't do the initialization in the constructor, you could make the field into an abstract property:
public abstract class A
{
protected abstract double Field { get; }
public double SquaredField { get { return Field * Field; } }
}
Now, the derived class has to implement the property, so you'll have it ready for the dependent SquaredField property. I would change the name though, since they're not fields anymore.
That's likely a signal that group of fields is more tightly coupled than A itself, and so should be moved to a class, say, AParams.
Then in A you can declare an abstract protected AParams createParams() method.
You could use a separate abstract function to accomplish this. The subclasses would be forced to implement it.
public abstract class A
{
protected double _field;
protected A()
{
InitializeField();
}
protected abstract void InitializeField();
public double SquaredField { get { return _field * _field; } }
}
public class B : A
{
protected override void InitializeField()
{
// Initialize...
}
}
I have three methods in two classes in which two methods have the same code but with different inheritance. How can I reduce the duplicate codes by adding a new class? I have tried to simplify it with a simple sample. Hope the code makes sense. How can I refactor this? Any help on this much appreciated.
GetConnection.cs
public class GetConnection1 : LookupConnection1
{
public override int GetConnectionCount()
{
/* This method is same as that of the GetConnection2 class file but inherits from other
class named LookUpConnection1
Need to refactor this duplicate method */
int conCount = base.GetConnectionCount();
int value = GetAvailableConnections(conCount);
return value;
}
private int GetAvailableConnections(int conCount)
{
/* This method is same as that in GetConnection2. This method is
exact replica that is in GetConnection2 class
Need to refactor this duplicate method */
int value = 0;
for (int i = 0; i < conCount; i++)
value = GetConnection(value);
return value;
}
private int GetConnection(int value)
{
/* This is the method which differs from the GetConnection2 class. */
return value + 10;
}
}
GetConnection2 class:
public class GetConnection2 : LookUpConnection2
{
public override int GetConnectionCount()
{
/* This method is same as that of the GetConnection1 class file but inherits from other
class named LookUpConnection2
Need to refactor this method*/
int conCount = base.GetConnectionCount();
int value = GetAvailableConnections(conCount);
return value;
}
private int GetAvailableConnections(int conCount)
{
/* This method is same as that in GetConnection1. This method is
exact replica that is in GetConnection1 class
Need to refactor this duplicate method */
int value = 0;
for (int i = 0; i < conCount; i++)
value = GetConnection(value);
return value;
}
private int GetConnection(int value)
{
/* This is the method which differs from the GetConnection2 class. */
return value + 30;
}
}
LookupConnection1 class file
public class LookupConnection1 : BaseConnection
{
public override int GetConnectionCount()
{
return 20;
}
}
LookUpConnection2 class file
public class LookUpConnection2 : BaseConnection
{
public override int GetConnectionCount()
{
return 10;
}
}
BaseConnection class file
public abstract class BaseConnection
{
public abstract int GetConnectionCount();
}
public class Program
{
static void Main()
{
GetConnection1 connection1 = new GetConnection1();
GetConnection2 connection2 = new GetConnection2();
Console.Write(connection1.GetConnectionCount());
Console.Write(connection2.GetConnectionCount());
}
}
Thanks in Advance,
You have to create an unique LookupConnection class and move GetAvailableConnections and GetConnection methods to the LookupConnection class. Below is the refactored code, hope it helps you!!!
// Create a unique LookupConnection class
public class LookupConnection : BaseConnection
{
// Private field to hold the initial value
private readonly int _count;
// Pass the expected value through the constructor.
// That will allow you to pass values as needed in each LookupConnection inheritance.
public LookupConnection(int count)
{
_count = count;
}
// Override GetConnectionCount method to use
// the GetAvailableConnections defined in this class
public override int GetConnectionCount()
{
return GetAvailableConnections(_count);
}
// If you put GetAvailableConnections in the LookupConnection class
// you don't need to worry about what it does, because always it does the same.
// Make it virtual if later you want change its behavior.
protected virtual int GetAvailableConnections(int conCount)
{
int value = 0;
for (int i = 0; i < conCount; i++)
value = GetConnection(value);
return value;
}
// Add the GetConnection to the LookupConnection and make it
// virtual in order to override in the different GetConnection classes
protected virtual int GetConnection(int value)
{
return value;
}
}
// This is how your class will look like
public class GetConnection1 : LookupConnection
{
// Pass the value to the base constructor as needed
public GetConnection1 () : base(20)
{
}
// Here you can override the GetConnection and add the particular behavior for this class
protected override int GetConnection(int value)
{
return value + 10;
}
}
// This is how your class will look like
public class GetConnection2 : LookUpConnection
{
// Pass the value to the base constructor as needed
public GetConnection2 () : base(10)
{
}
// Here you can override the GetConnection and add the particular behavior for this class
protected override int GetConnection(int value)
{
return value + 30;
}
}
public class Program
{
static void Main()
{
GetConnection1 connection1 = new GetConnection1();
GetConnection2 connection2 = new GetConnection2();
Console.Write(connection1.GetConnectionCount());
Console.Write(connection2.GetConnectionCount());
}
}
Okay so I am working on a project that haves a abstract public abstract bool IsFull { get; } this is how the school wants me to set it up. I was trying to figure out a work around that but I can't. I have a few files not sure if I want them all to post. so in my class it is inherited from a different class. so when I initiate it from the program cs class I can't get the boolean to change with a simple IsFull = true. I tried IsFull.Equal(true); but read that just a comparison attribute. I will show my code. Remember this is 100% new to me so if you asked questions why don't i do it this way the answer is I never was taught that lol.
So is there a way I can override it within the sweettooth class?
My Ninja class
using System.Collections.Generic;
using IronNinja.Interfaces;
namespace IronNinja.Models
{
abstract class Ninja
{
protected int calorieIntake;
public List<IConsumable> ConsumptionHistory;
public Ninja()
{
calorieIntake = 0;
ConsumptionHistory = new List<IConsumable>();
}
public abstract bool IsFull { get; }
public abstract void Consume(IConsumable item);
}
}
my inherited class sweettooth
using IronNinja.Interfaces;
namespace IronNinja.Models
{
class SweetTooth : Ninja
{
public string Name;
public SweetTooth(string name)
{
Name = name;
}
public override bool IsFull { get; }
public override void Consume(IConsumable item)
{
// provide override for Consume
int sweet = 0;
if (calorieIntake >= 1500)
{
}
else
{
if (item.IsSweet)
{
sweet = 10;
}
ConsumptionHistory.Add(item);
calorieIntake += item.Calories + sweet;
}
item.GetInfo();
}
}
}
Lastly my Programs .cs file
using System;
using IronNinja.Models;
namespace IronNinja
{
class Program
{
static void Main(string[] args)
{
Buffet hungryJack = new Buffet();
SweetTooth Albert = new SweetTooth("Alby");
while (!Albert.IsFull)
{
Albert.Consume(hungryJack.Serve());
}
foreach (Food item in Albert.ConsumptionHistory)
{
Console.WriteLine(item.Name);
System.Console.WriteLine(item.GetInfo());
}
}
}
}
From my understanding, the IsFull property can simply provide the logic to return whether or not the SweetTooth is full:
public override bool IsFull => calorieIntake >= 1500;
And then in SweetTooth.Consume you would check if they are full before consuming more consumables:
public override void Consume(IConsumable item)
{
// provide override for Consume
int sweet = 0;
if (IsFull)
{
return;
}
else
{
if (item.IsSweet)
{
sweet = 10;
}
ConsumptionHistory.Add(item);
calorieIntake += item.Calories + sweet;
}
item.GetInfo();
}
You simply can't, by language design. You can't make your subclass "more permissive" than the parent class.
If you want to assign IsFull property, you have to do it into the SweetTooth class through the constructor. Generally if you set a property with private setter is because you want to manage its state internally and do not let the client code to handle it.
Then, change the SweetTooth constructor as per below:
public SweetTooth(string name, bool isFull)
{
Name = name;
IsFull = isFull;
}
The alternative is to add a private backing field, but again you can edit this only internally:
private bool _isFull;
public override bool IsFull => _isFull;
The Equal method compares two values. In your specific case you called bool.Equals(bool) overload which worked as Albert.IsFull == true
I have trouble with figuring out how can I use generic types to solve my problem.
Also I don't know how to describe my problem in short so I will make simplified version of my problem as extended exmplanation.
I am making system for switching quality, transitioning quality levels between different types of 'component' class.
I have base class like:
public abstract class QualityLevel_Base
{
public bool Enabled = true;
public virtual void Transition(QualityLevel_Base a, QualityLevel_Base b, double value)
{
if (value >= 1) Enabled = b.Enabled; else if (value <= 0) Enabled = a.Enabled;
}
protected static double Lerp(double a, double b, double t) { return (1 - t) * a + t * b; }
}
Then I inherit from it like:
public sealed class QualityLevel_LightSource : QualityLevel_Base
{
public double Intensity;
public double Range;
public int ShadowsQuality;
public override void Transition(QualityLevel_Base a, QualityLevel_Base b, double value)
{
QualityLevel_LightSource la = a as QualityLevel_LightSource; // One part of my problem - avoid casting
QualityLevel_LightSource lb = b as QualityLevel_LightSource;
base.Transition(a, b, value);
Intensity = Lerp(la.Intensity, lb.Intensity, value);
/* etc... */
}
}
Then I want to manage quality levels in other class and be able to apply settings onto desired component class.
So I have base class to manage any count of quality levels:
public abstract class QualityManager_Base
{
public Component SourceComponent { get; protected set; }
public List<QualityLevel_Base> QualityLevels { get; protected set; }
public virtual void Initialize(Component component, int qualityLevelsCount)
{
QualityLevels = new List<QualityLevel_Base>();
SourceComponent = component;
AutoQualitySettings(qualityLevelsCount);
}
public virtual void AutoQualitySettings(int qualityLevelsCount) { }
public virtual void ApplyQualitySettings(QualityLevel_Base qualityLevel)
{
SourceComponent.Enabled = qualityLevel.Enabled;
}
}
And I inheriting it for LightSource like:
public sealed class QualityManager_LightSource : QualityManager_Base
{
public LightSource Light { get; private set; }
public override void Initialize(Component component, int qualityLevelsCount)
{
LightSource light = component as LightSource; // Another situation when I would like to avoid casting
Light = light;
base.Initialize(light, qualityLevelsCount);
}
public override void AutoQualitySettings(int qualityLevelsCount)
{
for (int i = 0; i < qualityLevelsCount; i++)
{
QualityLevel_LightSource lightSettings = new QualityLevel_LightSource();
lightSettings.Intensity = Light.Intensity;
lightSettings.Range = Light.Range;
lightSettings.ShadowsQuality = i / qualityLevelsCount;
if (i == qualityLevelsCount - 1) lightSettings.Enabled = false;
}
}
public override void ApplyQualitySettings(QualityLevel_Base qualityLevel)
{
base.ApplyQualitySettings(qualityLevel);
// To my Question: I want to use generic type to avoid casting
QualityLevel_LightSource lightSettings = qualityLevel as QualityLevel_LightSource;
Light.Intensity = lightSettings.Intensity;
Light.Range = lightSettings.Range;
Light.ShadowsQuality = lightSettings.ShadowsQuality;
}
}
Actually I managed to use generic types on this problem making stuff like:
public abstract class QualityLevel_Base<T> where T : QualityLevel_Base<T> { /*...*/ }
public class QualityLevel_LightSource : QualityLevel_Base<QualityLevel_LightSource> { /*...*/ }
public abstract class QualityManager_Base
{
public List<QualityLevel_Base> QualityLevels; // Would like to define it like that but I have to do it
// like that:
public abstract class QualityManager_Base<T> where T : QualityLevel_Base<T>
{
public List<QualityLevel_Base<T>> QualityLevels;
}
Then doing something like this causes error:
public abstract class QualityManager_Base<T> where T : QualityLevel_Base<T>
{
public List<QualityLevel_Base<T>> QualityLevels;
public virtual void AddComponentForQualityManager(Component comp)
{
if (QualityLevels == null) QualityLevels = new List<QualityLevel_Base<T>>();
LightSource light = comp as LightSource;
if (light != null)
{
QualityManager_LightSource lightManager = new QualityManager_LightSource();
QualityLevels.Add(lightManager); // Then I Can't do this because: "cannot convert from 'QualityManager_LightSource' to 'QualityLevel_Base<T>' "
}
/* ... */
}
}
"cannot convert from 'QualityManager_LightSource' to 'QualityLevel_Base'"
There is of course more going on in my system, it is just very simplified version to define my question: How can I avoid casting classes, how can I do it correctly?
Thanks!
I have researched this quite a bit on SO and Google. My C# app needs to be able to handle results of type long, decimal, and float. I've been exploring the option of making a generic interface, then closing it for each result type.
Here is the sample code:
using System;
interface IResult<T>
{
T Result();
void Increment(T value);
}
public class ResultLong : IResult<long>
{
private long result;
public long Result()
{
return result;
}
public void Increment(long value)
{
result += value;
}
}
public class App<T> where T : IConvertible
{
private IResult<T> result;
public void Run()
{
result = new ResultLong();
}
}
This gives the error:
Cannot implicitly convert type 'ResultLong' to 'IResult'. An explicit conversion exists (are you missing a cast?)
Adding a cast fixes the compiler error, but then the Increment method throws:
Cannot convert from int to T.
public void Run()
{
result = (IResult<T>)new ResultLong();
result.Increment(500);
}
Please let me know if this overall approach is valid, and if so, how I might make it work. If it's an invalid approach, what do you recommend?
Thanks!
Aron
I should also mention, this is how I'm currently handling it:
using System;
public class Result
{
public long ResultLong { get; set; }
public decimal ResultDecimal { get; set; }
public double ResultFloat { get; set; }
public DateTime ResultDateTime { get; set; }
public void Increment<T>(T value) where T : IConvertible
{
if (value is int || value is long)
{
ResultLong += value.ToInt64(null);
}
else if (value is decimal)
{
ResultDecimal += value.ToDecimal(null);
}
else if (value is double)
{
ResultFloat += value.ToDouble(null);
}
else if (value is DateTime)
{
ResultDateTime = value.ToDateTime(null);
}
}
}
And I should further mention that after looking into the suggestions, I decided to go with basic method overloading, and the app seems to be working fine so far.
public void Increment(int value)
{
ResultLong += value;
}
public void Increment(long value)
{
ResultLong += value;
}
public void Increment(double value)
{
ResultDouble += value;
}
public void Increment(decimal value)
{
ResultDecimal += value;
}
public void Increment(DateTime value)
{
ResultDateTime = value;
}
S.O. has been my primary guide in getting over many hurdles in learning C#. This was my first question and I greatly appreciate everyone's responses.
ResultLong is IResult<long> and not IResult<T>, so you get the error message.
Since you are insisting on using long, there really is no need for the generic type syntax (because you already know what the type is).
public class App
{
private IResult<long> result;
public void Run()
{
result = new ResultLong();
result.Increment(500);
}
}