I'm struggling with a small problem: I need a TreeDataScructure. So what I mean is: I have a base class which holds a list of (lets call it RootObject). And all of these RootObjects have RootObjects children which can have children by themselves etc.. And to all of the RootObject you can add different types of components.
I tried it like this:
Base class:
RootObject[] Roots;
RootObject class:
RootObject Parent;
RootObject[] Childs;
IGenericComponent[] Components;
The problem is: when I for example need to get all components in the Base class I get a stackoverflow because looping through each Root and their children takes a long time. The second problem is serializing. It would be hard to serialize it because some components use um-managed memory. My second approach was to make a list of Roots and Components in the base class and then just give an index as Parent and children, which one can access is from the list. But that got very confusing soon.
Does anybody know a good, fast and easy way to a TreeStructure like that?
PS: Here is all my code to achieve this:
//These structs are like indexes to the list in the base class
public struct ScoAutoStride : IAutoStride<Sco>
{
public int Index { get; internal set; }
public bool IsValid { get => KerboEngine.Scenery.SceneryObjects.Contains(KerboEngine.Scenery.SceneryObjects[Index]); }
public Sco Get()
{
return KerboEngine.Scenery.SceneryObjects[Index];
}
public void Set(Sco newValue)
{
KerboEngine.Scenery.SceneryObjects[Index] = newValue;
}
}
public struct CompAutoStride : IAutoStride<ScoComponent>
{
public int Index { get; internal set; }
public bool IsValid { get => KerboEngine.Scenery.SceneryObjectComponents.Contains(KerboEngine.Scenery.SceneryObjectComponents[Index]); }
public ScoComponent Get()
{
return KerboEngine.Scenery.SceneryObjectComponents[Index];
}
public ScoComponent<T> CorrectGet<T>() where T : class
{
return (ScoComponent<T>)Get();
}
public void Set(ScoComponent newValue)
{
KerboEngine.Scenery.SceneryObjectComponents[Index] = newValue;
}
}
//Components:
public abstract class ScoComponent
{
public Type ChildType { get; internal set; }
public string Name { get; set; }
public bool Enabled { get; set; } = true;
public ScoComponent() { }
}
public class ScoComponent<T> : ScoComponent where T : class
{
public T RawClass { get; set; }
protected void InitThis(T value)
{
RawClass = value;
ChildType = value.GetType();
Name = ChildType.Name;
}
public ScoComponent() : base() { }
}
//Root object:
public class Sco
{
public string Name { get; set; } = "NULL";
public ScoAutoStride Parent { get; internal set; }
public List<ScoAutoStride> Childs { get; internal set; }
public List<CompAutoStride> Components { get; internal set; }
public object Tag { get; set; }
public bool Enabled { get; set; } = true;
public bool Popped { get; set; } = false;
public const int MaxChilds = int.MaxValue;
public g_Vec3 Position { get; set; }
public g_Quatr Rotation { get; set; }
public g_Vec3 Scale { get; set; }
}
The base class:
public class Scenery
{
public string Name { get; internal set; }
public List<Sco> SceneryObjects { get; internal set; }
public List<ScoComponent> SceneryObjectComponents { get; internal set; }
public int ScoStride { get; private set; } = 0;
public int CompStride { get; private set; } = 0;
public Scenery() { }
}
If you're only dealing with around 10000 items, then your tree shouldn't be very deep (a perfectly balanced binary tree of this size is 14 levels deep, for example) and so you should absolutely not be getting stack overflow errors unless:
your tree is really a list (eg. totally unbalanced, with every node having a single child, 10000 levels deep)
your tree is really a directed cyclic graph (where an object can appear as a descendent of itself, so traversal will never terminate)
your tree traversal algorithm is broken.
You've said that (2) can't happen, and (1) would probably be obvious to you, so the problem seems to lie in your tree traversal code that you haven't shared with us.
Related
On one hand I have a list of capabilities, for example:
public interface ICapability
{
public string Name { get; }
}
public class RangeCapability<T> : ICapability
{
public string Name { get; set; }
public T Min { get; set; }
public T Max { get; set; }
}
public class SetCapability<T>
{
public string Name { get; set; }
public HashSet<T> Set { get; set; }
}
On the other hand I have a list of requirements
public interface IRequirement
{
public string Name { get; }
}
public class Requirement<T> : IRequirement
{
public string Name { get; set; }
public T Value { get; set; }
}
Both capability list may contain capabilities of different types T and requirement list may contain requirements of different types. The important thing is that if for a given name the underlying types match I should check if value is between min and max (for range class) or in a set like in the example below:
public class Entity
{
List<ICapability> Capabilities { get; set; }
public bool IsSatisfying(List<IRequirement> requirements)
{
foreach(var requirement in requirements)
{
var capability = Capabilities.FirstOrDefault(x => x.Name == requirement .Name);
//how to check if here if types match and if req. within range or in collection?
}
}
}
I am not sure how to match generic types of two different classes and then do the check suitable for the apropriate implementation (is within range/is present in set). Can somebody point me in the right direction how could I make it work?
I believe this is what you're looking for. Make the interfaces generic and also make the Entity class generic.
public interface INamed<T>
{
string Name { get; }
}
public interface ICapability<T> : INamed<T>
{
}
public class RangeCapability<T> : ICapability<T>
{
public string Name { get; set; }
public T Min { get; set; }
public T Max { get; set; }
}
public class SetCapability<T>
{
public string Name { get; set; }
public HashSet<T> Set { get; set; }
}
public interface IRequirement<T> : INamed<T>
{
}
public class Requirement<T> : IRequirement<T>
{
public string Name { get; set; }
public T Value { get; set; }
}
public class Entity<T>
{
List<ICapability<T>> Capabilities { get; set; }
public bool IsSatisfying(List<IRequirement<T>> requirements)
{
foreach (var requirement in requirements)
{
var capability = Capabilities.FirstOrDefault(x => x.Name == requirement.Name);
//how to check if here if types match and if req. within range or in collection?
if(capability is INamed<T>)
{
Console.WriteLine("types match");
}
}
}
}
I have the following construction of classes, here simplified as child classes of a 'mother' class called DataClass, which also contains one simple method:
public class DataClass
{
public int num { get; set; }
public string code { get; set; }
public PartClass part { get; set; }
public MemberClass member { get; set; }
public int Count()
{
Type t = typeof(DataClass);
return typeof(DataClass).GetProperties().Length;
}
}
public class PartClass
{
public int seriesNum { get; set; }
public string seriesCode { get; set; }
}
public class MemberClass
{
public int versionNum { get; set; }
public SideClass side { get; set; }
}
public class SideClass
{
public string firstDetail { get; set; }
public string secondDetail { get; set; }
public bool include { get; set; }
}
The issue is, I want to refactor the method so that it can give me an accurate counting of all properties found, including the ones in nested or child classes. In the above example, it only counts properties of DataClass, while I wanted it to return 2 for DataClass + 2 for PartClass + 1 for MemberClass + 3 for SideClass, sums up to 8 properties you may set through DataClass.
Can someone help me with this?
You can introduce interface with Count() method
public interface ICountable
{
int Count();
}
And use this interface to mark all types, which properties are participating in Count() calculation.
You can see the generic abstract class to implement this interface below. Generic T parameter is type whose properties need to be calculated. You implement a calculation logic only once and inherit this class where needed. You also go through all of properties, implementing ICountable, to calculate them as well (some kind of recursion)
public abstract class Countable<T> : ICountable
{
public int Count()
{
Type t = typeof(T);
var properties = t.GetProperties();
var countable = properties.Select(p => p.PropertyType).Where(p => typeof(ICountable).IsAssignableFrom(p));
var sum = countable.Sum(c => c.GetProperties().Length);
return properties.Length + sum;
}
}
and inherit it in your classes
public class DataClass : Countable<DataClass>
{
...
}
public class PartClass : Countable<PartClass>
{
...
}
public class MemberClass : Countable<MemberClass>
{
...
}
public class SideClass : Countable<SideClass>
{
...
}
And this is for the test
var dataClass = new DataClass();
var count = dataClass.Count();
It returns 8 as expected
I receive a list of Parent Objects(Devices) and would like to convert each Device Object into a subclass Object. The layout would looks something along the lines of:
public class Device
{
public string FimrwareVersion { get; set; }
public string DeviceName { get; set; }
public int Status { get; set; }
public string Alias { get; set; }
public string DeviceType { get; set; }
public string AppServerUrl { get; set; }
...
}
public class SmartLightBulb : Device
{
public string Model { get; set; }
public string Description { get; set; }
public string SoftwareVersion { get; set; }
public int State { get; set; }
// Turn On/Off
public async Task ToggleState()
{
// Toggle State
}
...
}
public class SmartPlug : Device
{
public string Model { get; set; }
public string Description { get; set; }
public string SoftwareVersion { get; set; }
public int State { get; set; }
// stay on for X
public async Task SetTimer()
{
// Set Timer
}
...
}
public class Lb100 : SmartLightBulb
{
public async Task ChangeBrightness(int brightness)
{
// Change Brightness
}
}
public class Lb200 : SmartLightBulb
{
public async Task ChangeBrightness(int brightness)
{
// Change Brightness
}
public async Task ChangeColor()
{
// Change Color
}
}
The issue is that I receive a list of Devices and I cannot downcast from Device to Lb100. I would Like for Lb100 to maintain all of the properties that were received from the Device Class, and also take the functionality of the Lb100. I have heard of reflection, but I have also heard that this is a very slow process and should be avoided when possible.
What would be perfect is if I could just go:
var device = new Device(){ Firmware = "V1.4"...};
var lb100 = (Lb100) device;
I also understand that the reason down casting is not possible is because when the parent object is created, it allocates just enough memory for the object of that type. Then when you try and cast it to a larger subclass, you are trying to fit that larger subclass into that allocated space.
From the research that I have gathered, this way of thinking when programming is incorrect, but no one really mentions the correct way of thinking through this issue. Other users mention that they create a constructor that manually sets each property equal to each other; but this seems like a major hassle for maintaining code, especially when more devices and models are being added. Thanks for any advice that you can provide!!
You can try to cast to subclass, you only need to verify if it can be casted.
public class Program
{
public static void Main(string[] args)
{
var smartLightBulb = new SmartLightBulb()
{
DeviceType = "deviceType",
Model = "model"
};
Output(smartLightBulb);
}
public static void Output(Device device)
{
var smartLightBulb = (device as SmartLightBulb);
if(smartLightBulb != null)
{
Console.WriteLine(smartLightBulb.Model);
}
}
}
public class Device
{
public string DeviceType { get; set; }
}
public class SmartLightBulb : Device
{
public string Model { get; set; }
}
You can down cast (from super to parent) as that's just removing fields.
E.g. an lb100 is a type of device.
But you can't up cast (from parent to super) as that needs additional data.
E.g. a device is not a type of lb100.
The sub class isn't derived from the main class, I'm just trying to differentiate them.
Even as I type this I can see it being impossible but I have some classes:
public class TransferServiceInformation {
public int ProviderId { get; set; }
public string PrePurchaseOverride { get; set; }
public bool PrePurchaseOverrideEnabled { get; set; }
}
and
public class TransferServiceProviderInformation {
public int ProviderId { get; set; }
public string PrePurchaseInfo { get; set; }
And I want it so that if I ever try to access myTransferServiceInformation.PrePurchaseOverride and PrePurchaseOverrideEnabled == false it should return PrePurchaseInfo from the TransferServiceProviderInformation with the same ID.
Is something like that even possible?
I'm just having a thought that a getter that requires a TransferServiceProviderInformation passed as an argument might work, and throw an exception if the IDs don't match. Is that the only solution? The thing is, I'd rather not have to dig through all the (thousands of lines of) code to change all the places were I (or someone else) has called this property.
This is just an idea:
Make a static list with instances inside your class and auto-fill it with using the constructor. Then you can check this list from outside for instances with the same id.
public class TransferServiceInformation
{
public int ProviderId { get; set; }
private string prePurchaseOverride;
public string PrePurchaseOverride
{
get
{
if(!PrePurchaseOverrideEnabled)
{
// Get instances from the other class where providerID matches
var instance = TransferServiceProviderInformation.Instances.Where(i => i.ProviderId == this.ProviderId).FirstOrDefault();
if(instance != null)
return (instance).PrePurchaseInfo;
}
return null; // If no match found
}
set
{
prePurchaseOverride = value;
}
}
private bool prePurchaseOverrideEnabled;
public bool PrePurchaseOverrideEnabled { get; set; }
}
public class TransferServiceProviderInformation
{
// Store your instances static
public static List<TransferServiceProviderInformation> Instances { get; set; }
public TransferServiceProviderInformation()
{
// Add every new instance to the list
Instances.Add(this);
}
public int ProviderId { get; set; }
public string PrePurchaseInfo { get; set; }
}
To-do's:
If an instance gets disposed, delete it from the list of instances.
I have next code that represents graph edges and nodes (simplified for question):
public class Node
{
}
public class Edge
{
public Node Source { get; set; }
public Node Target { get; set; }
}
Now I want to extend this classes for describing mine topology:
public class MineNode : Node
{
public double FanPressure { get; set; }
}
public class MineTunnel : Edge
{
public double Length { get; set; }
public double CrossSectionArea { get; set; }
public MineTunnel()
{
Source = new MineNode();
Target = new MineNode();
}
}
The problem is that I want to access additional data provided by MineNode when using Source and Target properties, but I can access only Node fields because they are declared in base class:
MineTunnel t = new MineTunnel();
Console.WriteLine(t.Source.FanPressure); //Error
The only way to access FanPressure is to cast Source to MineNode but code become ugly this way.
Console.WriteLine(((MineNode)t.Source).FanPressure); //OK
The another way is maybe to use somehow generics in base class declaration. But I'm not sure is that a good practice in my situation.
So, how can I solve such problem - extend functionality of base class fields?
Thanks.
You could define your Edge type as generic, with constraints:
public class Edge<TNode> where TNode: Node
{
public TNode Source { get; set; }
public TNode Target { get; set; }
}
Through which you could redefine your MineTunnel type as:
public class MineTunnel : Edge<MineNode>
{
// Stuff
}
I think generics is the way to go here...
Try this:
public class Node
{
}
public class Edge<S, T>
where S : Node
where T : Node
{
public S Source { get; set; }
public T Target { get; set; }
}
Then you can extend the Node and Edge classes with:
public class MineNode : Node
{
public double FanPressure { get; set; }
}
public class MineTunnel : Edge<MineNode, MineNode>
{
public double Length { get; set; }
public double CrossSectionArea { get; set; }
public MineTunnel()
{
Source = new MineNode();
Target = new MineNode();
}
}
Please correct me if this is wrong or doesn't work... :)
MineTunnel t = new MineTunnel();
Console.WriteLine(t.Source.FanPressure); // Now this works without errors ;)