Pass variable to class implemented in a script loaded with Roslyn - c#

I'm trying to implement a script evaluator to evaluate some classes that my users will implement that will have a fixed entry point accepting a variable that will be given from the main application.
To do this I've declared a namespace that contains the type of the object that will be passed to the entry point. This is the whole code that declares the custom type and executes the scripts:
namespace MyNamespace
{
public class Vertex
{
public float X { get; } = 0.0f;
public float Y { get; } = 0.0f;
public float Z { get; } = 0.0f;
public Vertex(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return string.Format("[{0}, {1}, {2}]", X, Y, Z);
}
}
}
public class Globals
{
public Vertex testVertex = new Vertex(0, 0, 0);
public int testValue = 100;
}
class Program
{
static void Main(string[] args)
{
try
{
ScriptOptions options = ScriptOptions.Default;
// Add assemblies
options = options.AddReferences(
typeof(System.Object).GetTypeInfo().Assembly,
typeof(System.Linq.Enumerable).GetTypeInfo().Assembly,
typeof(MyNamespace.Vertex).GetTypeInfo().Assembly
);
// Add usings
options = options.AddImports("System");
options = options.AddImports("System.Linq");
options = options.AddImports("System.Collections.Generic");
options = options.AddImports("MyNamespace");
var script = <added below>;
Globals globals = new Globals();
ScriptState<object> scriptState = CSharpScript.RunAsync(script, options: options, globals: globals).Result;
scriptState = scriptState.ContinueWithAsync(#"Figure fig = new Figure();").Result;
scriptState = scriptState.ContinueWithAsync(#"fig.Draw(testVertex);").Result;
}
catch (CompilationErrorException e)
{
Console.WriteLine(string.Join(Environment.NewLine, e.Diagnostics));
}
}
}
And this is an example of a script that I'm trying to load:
public class Figure
{
public void Draw(Vertex v)
{
System.Console.WriteLine(v.ToString());
}
public void Draw(int i)
{
System.Console.WriteLine(i);
}
}
Now, the problem is that when I call the method Draw of the class Figure inside the script, it throws me an exception saying
error CS1503: Argument 1: cannot convert from 'MyNamespace.Vertex [~/Projects/ScriptTest/ScriptTest/bin/Debug/netcoreapp1.0/ScriptTest.dll]' to 'MyNamespace.Vertex [ScriptTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]'
When I do
scriptState = scriptState.ContinueWithAsync(#"fig.Draw(testValue);").Result; // Using testValue instead of testVertex
everything works fine.
It think the problem is that the script is referring to a different namespace than MyNamespace but I don't know how to solve this.

Related

Make struct pointing to different struct in C# [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 months ago.
Improve this question
class IntComponent
{
public int size;
}
class IntReferenceComponent : IntComponent
{
public IntComponent target; // keep my size same as target size
private void OnValidate()
{
//triggered on target assignment
}
}
Is it possible in C# make struct variable pointing to different struct variable like with objects ? Even with unsafe pointers ?
Edit
The final solution according to Charlieface solution:
public abstract class StructComponent<T> where T : struct
{
public T size;
}
public class IntComponent : StructComponent<int>{ }
public class IntReferenceComponent : IntComponent
{
public IntComponent target;
public new int size
{
get => target.size;
set => target.size = value;
}
}
In Unity I had to create custom editor:
public abstract class StructComponentEditor<T, D> : Editor where T : struct where D : StructComponent<T>
{
public static object StructField(string label, T value, params GUILayoutOption[] options)
{
switch (value)
{
case Vector3 v:
return EditorGUILayout.Vector3Field(label, v, options);
case Vector2 v:
return EditorGUILayout.Vector2Field(label, v, options);
case float f:
return EditorGUILayout.FloatField(label, f, options);
case int i:
return EditorGUILayout.IntField(label, i, options);
case double d:
return EditorGUILayout.DoubleField(label, d, options);
case Color c:
return EditorGUILayout.ColorField(label, c, options);
}
return null;
}
public void DrawDefaultStructComponent()
{
D target = serializedObject.targetObject as D;
PropertyInfo sizeProp = target.GetType().GetProperty("size");
object value;
try
{
value = sizeProp.GetValue(target);
}
catch
{
value = new T();
}
if(sizeProp.SetMethod != null)
{
sizeProp.SetValue(target, StructField("Size", (T)value));
return;
}
StructField("Size", (T)value);
}
public override void OnInspectorGUI()
{
DrawDefaultStructComponent();
DrawDefaultInspector();
}
}
[CustomEditor(typeof(StructComponent<int>), true)]
[CanEditMultipleObjects]
public class IntComponentEditor : StructComponentEditor<int, StructComponent<int>> { }
I need to write something because most of it is just code :)))))) So thanks to everyone who helped :) I love you <3
You don't need to do this in your case, and generally it would be ill-advised to try and mess around with pointers in normal Object Oriented cases, as you don't normally expose the internals of a class.
Instead, just use composition, with an outer property exposing the value of the inner object
class IntReferenceComponent : IntComponent
{
public IntComponent target; // keep my size same as target size
public int Size
{
get => target.size;
set => target.size = value;
}
}
You Can not do this with struct since its value type means you only can take a copy from it and you can't use it as a pointer as you do with classes
Below Code shows that even with pointers unsafe code will not work because you, in the end, will copy the values to a struct to use them,
since you can't access the object attribute with the pointer that
just `point to the object itself (this is what I know )
namespace Feto
{
internal struct Complex
{
public float real;
public float imag;
public Complex(float real, float image)
{
this.real = real;
this.imag = image;
}
public override string ToString()
{
return $" Class Complex {this.real} , {this.imag}";
}
}
class FixingComplex
{
public float real;
public float imag;
public FixingComplex(float real, float image)
{
this.real = real;
this.imag = image;
}
public override string ToString()
{
return $" Class Complex {this.real} , {this.imag}";
}
}
unsafe class program
{
public static void Main()
{
Complex x = new Complex(10, 20);
Console.WriteLine(x);
Complex* y = &x;
//address of stuct
Console.WriteLine((int)&x);
//the y point to it
Console.WriteLine((int)y);
//what z point to
Console.WriteLine(*y);
//send the addresss of the stuct
addNumbers(y);
Console.WriteLine(x);
Console.ReadKey();
void addNumbers(Complex* result)
{
//make sure it is the same address of stuct
Console.WriteLine((int)result);
//now here we got the address of struct we need to modify it
Console.WriteLine(*result);
//Here is the problem it will take copy
var value = *result;
value.real = 8888;
value.imag = 8888;
//you can use fixedcomplex to go on with pointers and workaround
.....
}
}
}
}
But there is a Solution
with small modifications like passing the address as a reference, not a copy of the address this will workaround and change the values and
The Changes addNumbers(ref y); void addNumbers(ref Complex* result) *result = new Complex(8888,8888);
namespace Feto
{
internal struct Complex
{
public float real;
public float imag;
public Complex(float real, float image)
{
this.real = real;
this.imag = image;
}
public override string ToString()
{
return $" Class Complex {this.real} , {this.imag}";
}
}
class FixingComplex
{
public float real;
public float imag;
public FixingComplex(float real, float image)
{
this.real = real;
this.imag = image;
}
public override string ToString()
{
return $" Class Complex {this.real} , {this.imag}";
}
}
unsafe class program
{
public static void Main()
{
Complex x = new Complex(10, 20);
Console.WriteLine(x);
Complex* y = &x;
//address of stuct
Console.WriteLine((int)&x);
//the y point to it
Console.WriteLine((int)y);
//what z point to
Console.WriteLine(*y);
//send the addresss of the stuct
addNumbers(ref y);
Console.WriteLine(x);
Console.ReadKey();
void addNumbers(ref Complex* result)
{
//make sure it is the same address of stuct
Console.WriteLine((int)result);
//now here we got the address of struct we need to modify it
Console.WriteLine(*result);
//Here is the problem it will take copy
//var value = *result;
*result = new Complex(8888,8888);
//you can use fixedcomplex to go on with pointers and workaround
}
}
}
}

Extension method on ObservableCollection<MyType>, MyType : IEnumerable<MyType>

I'm having trouble creating an extension method on an ObservableCollection for a custom type. The extension method I need to create is the "ToAnotherType" kind (as in, ToList, ToArray). The MyPoint example implements the IEnumerable interface, but I think I am not exposing the yield correctly?
The real thing obviously has more stuff going on, this is just a stripped down sample in a console app to identify the issue. I've tried changing the OC to a regular List to see if something was going on there, but it's not.
I see many of the "How to make your class Enumerable" examples create a second class derived from List (IE, public class MyPointList : List) but that seems wasteful when the original type can handle it itself, or have it pushed off in a partial class file.
It all looks like it's working until the foreach in the extension method itself- Where I get an error saying 'MyPoint' does not contain a definition for 'X' and 'Y'.
I could obviously handle the conversion with a method that takes in a List and returns a List, but it would be really nice to have the extension.
references as to how I ended up with code I did:
https://www.codeproject.com/Articles/474678/A-Beginners-Tutorial-on-Implementing-IEnumerable-I
https://dotnetcodr.com/2015/07/24/implementing-an-enumerator-for-a-custom-object-in-net-c/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Collections;
namespace EnumerableDemo
{
class Program
{
static void Main(string[] args)
{
var myPoints = new ObservableCollection<MyPoint>
{
new MyPoint(10, 10),
new MyPoint(20, 20),
new MyPoint(30, 30),
new MyPoint(40, 40),
new MyPoint(50, 50)
};
Console.WriteLine("Print a single point via extension method:");
PrintSinglePointToConsole(myPoints[0].ToPoint());
Console.WriteLine("");
Console.WriteLine("Print the whole OC of points:");
PrintPointsToConsole(myPoints.ToPoints());
Console.ReadLine();
}
public static void PrintSinglePointToConsole(Point point)
{
Console.WriteLine("Point {0},{1}", point.X, point.Y);
}
public static void PrintPointsToConsole(List<Point> points)
{
foreach (var item in points)
{
Console.WriteLine("Point: {0},{1}", item.X, item.Y);
}
}
}
public class MyPoint : IEnumerable<MyPoint>
{
private List<MyPoint> _myPoints = new List<MyPoint>();
private int _x { get; set; } = 0;
public int X { get { return _x; } set { _x = value; } }
private int _y { get; set; } = 0;
public int Y { get { return _y; } set { _y = value; } }
public MyPoint()
{
}
public MyPoint(int x, int y)
{
_x = x;
_y = y;
}
public IEnumerator<MyPoint> GetEnumerator()
{
foreach (var item in _myPoints)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static class MyPointExtension
{
public static Point ToPoint(this MyPoint point)
{
return new Point(point.X, point.Y);
}
public static List<Point> ToPoints<MyPoint>(this ObservableCollection<MyPoint> list)
{
var result = new List<Point>();
foreach (var item in list)
{
//Line with error:
//'MyPoint' Does not contain a definition for 'X' and no extension method for
//'X' accepting a first argument type of 'MyPoint' could be found.
result.Add(new Point(item.X, item.Y));
}
return result;
}
}
}
You don't need a MyPoint generic parameter in ToPoints.
Just use
public static List<Point> ToPoints(this ObservableCollection<MyPoint> list)
And the result is:
Print a single point via extension method:
Point 10,10
Print the whole OC of points:
Point: 10,10
Point: 20,20
Point: 30,30
Point: 40,40
Point: 50,50
BTW, you can also make a code a bit cleaner and shorter if you discard the _x and _y fields, like this:
public int X { get; set; } = 0;
public int Y { get; set; } = 0;
public MyPoint(int x, int y)
{
X = x;
Y = y;
}
Final code block, which makes use of the other extension method ToPoint
public static List<Point> ToPoints(this ObservableCollection<MyPoint> list)
{
var result = new List<Point>();
foreach (var item in list)
{
result.Add(item.ToPoint());
}
return result;
}

Method doesn't set value of an object

I'm new to C# and have no idea why doesn't it work. I'm trying to create 3 methods that create a new Vehicle object with different properties. I've tried to do it with polymorphism but it's been even worse. I bet the answer to that is so easy..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace PetrolStation
{
class Program
{
static void Main(string[] args)
{
Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
aTimer.Interval = 1000; // 1000=1s
aTimer.Enabled = true;
Console.ReadLine();
void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
Random rand = new Random();
int temp = rand.Next(1, 3);
if (temp == 2)
{
Vehicles vehicle = new Vehicles();
vehicle.newCar();
Console.Out.WriteLine("test", vehicle.carType); // it should print "car" after test but it doesn't
}
if (temp == 3)
{
Vehicles vehicle = new Vehicles();
vehicle.newVan();
Console.Out.WriteLine("test", vehicle.carType);// it should print "van" after test but it doesn't
}
}
}
}
Second class:
public class Vehicles
{
public string carType;
public string fuelType;
public int tankCap;
public double fuelInTank;
public Random rand = new Random();
public void newCar()
{
carType = "Car";
tankCap = 40;
fuelInTank = rand.NextDouble() * 10;
int tempFuelType = rand.Next(1, 3);
switch (tempFuelType)
{
case 1:
fuelType = "petrol";
break;
case 2:
fuelType = "Diesel";
break;
case 3:
fuelType = "LPG";
break;
}
}
public void newVan()
{
carType = "van";
tankCap = 80;
fuelInTank = rand.NextDouble() * 20;
int tempFuelType = rand.Next(1, 2);
if (tempFuelType == 1)
{
fuelType = "Diesel";
}
else
{
fuelType = "LPG";
}
}
using System;
namespace StackOverflow_OOP
{
class Program
{
// Randomness removed: you want a driver to **consistently** pass or fail.
static void Main(string[] args)
{
Car car = new Car(VehicleFuelType.Petrol, 20);
// The first arg specifies format/placement of the second
Console.Out.WriteLine("Vehicle Type: {0}", car.Type);
// In background, uses String.Format()
// See https://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx
Van van = new Van(VehicleFuelType.Diesel, 40);
Console.Out.WriteLine("Vehicle Type: {0}", van.Type);
Console.ReadLine();
}
}
// A string that only takes a small number of values is called an enumeration
// See https://msdn.microsoft.com/en-us/library/sbbt4032.aspx
public enum VehicleFuelType
{
Petrol,
Diesel,
LPG
}
// Vehicle is clearly abstract in this context, while Car & Van are concrete.
// See explaination after code.
public abstract class Vehicle
{
public VehicleFuelType FuelType { get; }
public int TankCap { get; }
public double FuelInTank { get; private set; }
public string Type { get { return this.GetType().Name; } }
public Vehicle(VehicleFuelType fuelType, int tankCap, double fuelInTank)
{
FuelType = fuelType;
TankCap = tankCap;
FuelInTank = fuelInTank;
}
}
public class Car : Vehicle
{
public Car(VehicleFuelType fuelType, double fuelInTank) : base(fuelType, 40, fuelInTank)
{
}
}
public class Van : Vehicle
{
public Van(VehicleFuelType fuelType, double fuelInTank) : base(fuelType, 80, fuelInTank)
{
}
}
}
Classes: Abstract vs. Concrete
public abstract class Shape
{
public abstract double GetArea();
}
public class Circle : Shape
{
public int Radius { get; }
public Circle(int radius)
{
Radius = radius;
}
public override double GetArea()
{
return Math.PI * Radius * Radius;
}
}
public class Square : Shape
{
public int SideLength { get; }
public Square(int sideLength)
{
SideLength = sideLength;
}
public override double GetArea()
{
return SideLength * SideLength;
}
}
The simplest difference between an abstract class and a concrete one:
An abstract class cannot be instantiated (directly); a concrete one can.
For instance,
Shape shape = new Shape(); // impossible
Circle circle = new Circle(); // fine
Crucially,
Shape circle = new Circle(); // fine
Conceptually, it's impossible to create an abstract class (e.g. shape) without actually creating a concrete class (circle).
Even more simply, imagine going to a restaurant and telling the waiter you want "food". Obviously he can comply, but somewhere along the line "food" must become steak or tuna or spaghetti, etc.
Regarding the direct problem you mentioned:
The problem is related to the WriteLine call.
The signature of the call you mane
Console.Out.WriteLine("test", vehicle.carType);
is
public virtual void WriteLine(string format, object arg0)
So the first parameter in this call should have an item which you want to composite (see https://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx for more details).
bottom line the call should be something like
Console.Out.WriteLine("test {0}", vehicle.carType);
Regarding the "polymorphism" you mentioned:
What you implemented isn't polymorphism.
You might want to read about polymorphism a little bit:
oop: https://msdn.microsoft.com/en-us/library/mt656686.aspx
polymorphism: https://msdn.microsoft.com/en-us/library/ms173152.aspx

How to automate unit testing of object state?

I have this serializable class which is my class for persisting game data.
[Serializable]
class GameData
{
public float experience = Helper.DEFAULT_EXPERIENCE;
public float score = Helper.DEFAULT_SCORE;
public float winPercent = Helper.DEFAULT_WIN_PERCENT;
public int tasksSolved = Helper.DEFAULT_NUM_OF_TASKS_SOLVED;
public int correct = Helper.DEFAULT_NUM_OF_CORRECT;
public int additions = Helper.DEFAULT_NUM_OF_ADDITIONS;
public int subtractions = Helper.DEFAULT_NUM_OF_SUBTRACTIONS;
public bool useAddition = Helper.DEFAULT_USE_ADDITION;
public bool useSubtraction = Helper.DEFAULT_USE_SUBTRACTION;
public bool useIncrementalRange = Helper.DEFAULT_USE_INCREMENTAL_RANGE;
public bool gameStateDirty = Helper.DEFAULT_GAME_STATE_DIRTY;
public bool gameIsNormal = Helper.DEFAULT_GAME_IS_NORMAL;
public bool operandsSign = Helper.DEFAULT_OPERANDS_SIGN;
}
The class that utilizes this serializable class looks like this:
using UnityEngine;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class SaveLoadGameData : MonoBehaviour
{
public static SaveLoadGameData gameState;
public float experience = Helper.DEFAULT_EXPERIENCE;
public float score = Helper.DEFAULT_SCORE;
public float winPercent = Helper.DEFAULT_WIN_PERCENT;
public int tasksSolved = Helper.DEFAULT_NUM_OF_TASKS_SOLVED;
public int correct = Helper.DEFAULT_NUM_OF_CORRECT;
public int additions = Helper.DEFAULT_NUM_OF_ADDITIONS;
public int subtractions = Helper.DEFAULT_NUM_OF_SUBTRACTIONS;
public bool useAddition = Helper.DEFAULT_USE_ADDITION;
public bool useSubtraction = Helper.DEFAULT_USE_SUBTRACTION;
public bool useIncrementalRange = Helper.DEFAULT_USE_INCREMENTAL_RANGE;
public bool gameStateDirty = Helper.DEFAULT_GAME_STATE_DIRTY;
public bool gameIsNormal = Helper.DEFAULT_GAME_IS_NORMAL;
public bool operandsSign = Helper.DEFAULT_OPERANDS_SIGN;
void Awake () {}
public void init ()
{
if (gameState == null)
{
DontDestroyOnLoad(gameObject);
gameState = this;
}
else if (gameState != this)
{
Destroy(gameObject);
}
}
public void SaveForWeb ()
{
UpdateGameState();
try
{
PlayerPrefs.SetFloat(Helper.EXP_KEY, experience);
PlayerPrefs.SetFloat(Helper.SCORE_KEY, score);
PlayerPrefs.SetFloat(Helper.WIN_PERCENT_KEY, winPercent);
PlayerPrefs.SetInt(Helper.TASKS_SOLVED_KEY, tasksSolved);
PlayerPrefs.SetInt(Helper.CORRECT_ANSWERS_KEY, correct);
PlayerPrefs.SetInt(Helper.ADDITIONS_KEY, additions);
PlayerPrefs.SetInt(Helper.SUBTRACTIONS_KEY, subtractions);
PlayerPrefs.SetInt(Helper.USE_ADDITION, Helper.BoolToInt(useAddition));
PlayerPrefs.SetInt(Helper.USE_SUBTRACTION, Helper.BoolToInt(useSubtraction));
PlayerPrefs.SetInt(Helper.USE_INCREMENTAL_RANGE, Helper.BoolToInt(useIncrementalRange));
PlayerPrefs.SetInt(Helper.GAME_STATE_DIRTY, Helper.BoolToInt(gameStateDirty));
PlayerPrefs.SetInt(Helper.OPERANDS_SIGN, Helper.BoolToInt(operandsSign));
PlayerPrefs.Save();
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
public void SaveForX86 () {}
public void Load () {}
public void UpdateGameState () {}
public void ResetGameState () {}
}
Note: GameData is inside the same file with SaveLoadGameData class.
As you can see GameData class has ton of stuff and creating test for each function inside SaveLoadGameData class is long and boring process. I have to create a fake object for each property inside GameData and test the functionality of the functions in SaveLoadGameData do they do what they are supposed to do.
Note: This is Unity3D 5 code and testing MonoBehaviors with stubs and mocks is almost immposible. Therefore I created helper function that creates fake object:
SaveLoadGameData saveLoadObject;
GameObject gameStateObject;
SaveLoadGameData CreateFakeSaveLoadObject ()
{
gameStateObject = new GameObject();
saveLoadObject = gameStateObject.AddComponent<SaveLoadGameData>();
saveLoadObject.init();
saveLoadObject.experience = Arg.Is<float>(x => x > 0);
saveLoadObject.score = Arg.Is<float>(x => x > 0);
saveLoadObject.winPercent = 75;
saveLoadObject.tasksSolved = 40;
saveLoadObject.correct = 30;
saveLoadObject.additions = 10;
saveLoadObject.subtractions = 10;
saveLoadObject.useAddition = false;
saveLoadObject.useSubtraction = false;
saveLoadObject.useIncrementalRange = true;
saveLoadObject.gameStateDirty = true;
saveLoadObject.gameIsNormal = false;
saveLoadObject.operandsSign = true;
return saveLoadObject;
}
How would you automate this process?
Yes two asserts inside one test is a bad practice but what would you do instead?
Example test for SaveForWeb()
[Test]
public void SaveForWebTest_CreateFakeGameStateObjectRunTheFunctionAndCheckIfLongestChainKeyExists_PassesIfLongestChainKeyExistsInPlayerPrefs()
{
// arrange
saveLoadObject = CreateFakeSaveLoadObject();
// act
saveLoadObject.SaveForWeb();
// assert
Assert.True(PlayerPrefs.HasKey(Helper.LONGEST_CHAIN_KEY));
Assert.AreEqual(saveLoadObject.longestChain, PlayerPrefs.GetInt(Helper.LONGEST_CHAIN_KEY, Helper.DEFAULT_LONGEST_CHAIN));
GameObject.DestroyImmediate(gameStateObject);
}
Since Helper is static class containing only public constants I had to use BindingFlags.Static and BindingFlags.Public to iterate over its members, so I used this code snippet to automate asserting over several fields of different type:
FieldInfo[] helperFields = typeof(SaveLoadGameData).GetFields();
FieldInfo[] defaults = typeof(Helper).GetFields(BindingFlags.Static | BindingFlags.Public);
for(int i = 0; i < defaults.Length; i += 1)
{
Debug.Log(helperFields[i].Name + ", " + helperFields[i].GetValue(saveLoadObject) + ", " + defaults[i].GetValue(null));
Assert.AreEqual(helperFields[i].GetValue(saveLoadObject), defaults[i].GetValue(null));
}
Note: defaults and helperFields have the same length as I am checking if helperFields have the default values after using ResetGameState().
Though this answer is about ResetGameState() instead of SaveForWeb() function, this code can be applied wherever possible.

Vector math dimension consistency check at compile-time

I am creating a linear algebra library in C#, and I would like to force dimension inconsistency errors up to compile-time. I've implemented a similar solution to this, where the trait I use is a class that uniquely maps to an integer. The problem is for every possible size I would like my Vectors to have, I would need to create a class with a unique name to represent it.
Here is an example of that implementation:
public class Vector<T> where T: ISize, new()
{
static readonly T size = new T();
List<double> values;
public Vector(List<double> values)
{
if (values.Count != size.Size)
throw new IndexOutOfRangeException();
this.values = new List<double>(values);
}
public double Get(int index)
{
return values[index];
}
public Vector<T> Add(Vector<T> other)
{
var vv = new List<double>();
for (int ii = 0; ii < size.Size; ++ii)
vv.Add(other.Get(ii) + this.values[ii]);
return new Vector<T>(vv);
}
}
public interface ISize
{
int Size { get; }
}
public class S1 : ISize
{
public int Size
{
get { return 1; }
}
}
public class S2 : ISize
{
public int Size
{
get { return 2; }
}
}
And here's an example of its usage:
class Program
{
static void Main(string[] args)
{
var v1 = new Vector<S2>(new List<double>() { 1, 2 });
var v2 = new Vector<S2>(new List<double>() { 10, -4 });
var z1 = new Vector<S1>(new List<double>() { 10 });
// works
var v3 = v1.Add(v2);
// complie-time error
var z2 = z1.Add(v1);
}
}
This works quite well for my purposes, except for the fact that I would need to create a different implementation of ISize for every possible Vector size. Is there any way for me to implement the Vector class that would allow me to get around this problem?
In order to get a compile-time error, you need to have different types. C# does not have a concept that let's you define a type parameter that itself takes a kind of value parameters - which is what you would need to do this.
Therefore, I don't think what you are asking is possible.
I think there might be a way to make unique types for family of vector instances using anonymous types, but that's going to be quirky and I don't think it would provide the type safety that you want.
C++ has such a concept in templates (so it's not unreasonable), just not possible in C#.
You can create a single N-dimentional Vector class with compile time type checking, but it's pretty messy. What we're creating here is LISP style linked-lists, but through generic type arguments rather than purely out of object references via fields.
public interface IVector
{
double Value { get; }
IVector Tail { get; }
}
public class Vector<T> : IVector
where T : IVector
{
internal Vector(double value, T tail)
{
Value = value;
Tail = tail;
}
public double Value { get; private set; }
public T Tail { get; private set; }
public Vector<Vector<T>> Add(double value)
{
return new Vector<Vector<T>>(value, this);
}
}
internal class EmptyVector : IVector
{
public double Value
{
get { throw new NotImplementedException(); }
}
public IVector Tail
{
get { return null; }
}
}
public static class Vector
{
public static readonly Vector<IVector> Empty = new Vector<IVector>(
0, new EmptyVector());
public static IEnumerable<double> AllValues(this IVector vector)
{
IVector current = vector;
while (current != Vector.Empty && current != null)
{
yield return current.Value;
current = current.Tail;
};
}
}
This allows us to write:
var v1 = Vector.Empty.Add(1).Add(2);
var v2 = Vector.Empty.Add(10).Add(-4);
var z1 = Vector.Empty.Add(10);
v1 = v2;//works, as they are the same type
z1 = v2;//fails, as they aren't the same type, since they're a different size
This allows allows you to write a method that accepts a vector of a particular size. It's not convenient, and it doesn't scale, but it works. If you want, say, a 3D vector as a parameter, you can write:
public static void Foo(Vector<Vector<Vector<IVector>>> vector)
{
var first = vector.Value;
var second = vector.Tail.Value;
var third = vector.Tail.Tail.Value;
}

Categories