I have a simple Gun class and i want to have attachable components to it. So i use a generic type for my Attatch method since their are different component types.
My problem is i don't know how to assign the value to the object in the method - i think i might have misunderstood the logic of generics. This is my code:
public class Gun : Weapon
{
public Magazine magazine { private set; get; }
public void Attach<T>(T component) //validate if can be attatched
{
if (component is Magazine)
{
if (magazine == null)
Apply(component);
else
{
Drop(component);
Apply(component);
}
}
}
private void Apply<T>(T component) // apply the component to the gun
{
if (component is Magazine)
{
magazine = (Magazine)component; //ERROR: Cannot convert type `T' to `Magazine'
}
}
...// extra non relevant stuff
}
My Magazine type is a class not a struct if that matters:
public class Magazine : Weapon_Component {//code }
So how do i correct for this error so it will be functional for any weapon component type?
It is difficult to understand what design would work best in your scenario. You seem to be asking about a specific syntactical problem. There's not much detail in your question about the broader issues. However, making some inferences, here are a couple of options you might prefer to what you're doing now…
1. Use a dictionary to track components
public class Gun : Weapon
{
private readonly Dictionary<Type, Weapon_Component> _components
= new Dictionary<Type, Weapon_Component>();
public void Attach(Weapon_Component component) //validate if can be attatched
{
if (_components.ContainsKey(component.GetType()))
{
Drop(component);
}
Apply(component);
}
private void Apply(Weapon_Component component) // apply the component to the gun
{
_components.Add(component.GetType(), component);
}
private void Drop(Weapon_Component component)
{
_components.RemoveKey(component.GetType());
}
}
Note that the Attach(), Apply(), and Drop() methods could all be consolidated into a single assignment:
public void Attach(Weapon_Component component)
{
_components[component.GetType()] = component;
}
Though, you might want to keep the Drop() method, for the scenario where you want to remove the component without replacing it. It's not clear from your question whether that ever happens.
2. Design by interface and let components interact directly with weapons
interface IWeapon { }
interface IGun : IWeapon
{
Magazine Magazine { get; set; }
}
interface IWeaponComponent
{
Attach(IWeapon weapon);
}
public class Gun : Weapon, IGun
{
public Magazine Magazine { get; set; }
}
public class Magazine : IWeaponComponent
{
public Attach(IWeapon weapon)
{
IGun gun = weapon as IGun;
if (gun == null)
{
throw new ArgumentException("Cannot attach a magazine to weapon that is not a gun");
}
gun.Magazine = this;
}
}
Each of the above has its pros and cons, and of course these are not by any means your only options. I'm just trying to give you some ideas of what you might do, as well as help you understand, if these are not helpful to you, how you might narrow your question so that it's more answerable.
If you're components must always be of type Magazine and solely that type, than there is absolutely no use of generics here.
If you want any type that is or inherits the type Magazine than yes generics can help, by constraining the generic parameter to inherit the said type like this:
where T : Magazine
But you can still make the type not generic but simply Magazinethis should still do the job.
Or to fix your specific case you can do:
var magazine1 = component as Magazine;
if (magazine1 != null)
{
magazine = magazine1;
}
But in your example it makes more sense to use one of the former 2 options, rather than the solution you already have.
Related
So im trying to use compToGet string that have been passed through the parameter into slot.GetComponent().level++;
upgradeFoundation() will be called on button click.
and there is actually quite a lot of buttons with similar functionality (like: upgradeTurret(), upgradeTurret2(), etc)
thats why im trying to change the value of compToget string base on which button you click and use that new string to get component under the name of that new string but it seems it doesn't work that way and I dont know how it would work any other way, any help would be much appreciate.
public void upgradeFoundation()
{
float upgFoundationCost = slotGroup.transform.Find(slotName).gameObject.GetComponent<Slot>().upgFoundationCost;
Upgrade(upgFoundationCost, "Foundation");
}
public void Upgrade(float upgCost, string compToGet)
{
GameObject slot = slotGroup.transform.Find(slotName).gameObject;
if (inGameUIManagerScript.money >= upgCost)
{
Type compToGetType = Type.GetType(compToGet); //im not sure how to convert a string into a type
slot.GetComponent<compToGetType>().level++; //this is the error line saying im treating a var like a type
}
}
Thank you in advance.
Exactly the same issue as in your previous question => You can not use the generic! Instead use GetComponent(compToGetType);
However I removed the duplicate since you still would need to cast to your actual type which is anything but trivial!
=> Again I can only recommend: Don't use strings!
Rather have a common Base class or interface like e.g.
public abstract class BaseComponent : MonoBehaviour
{
private int level;
// public read-only access
public int Level => level;
public virtual void Upgrade()
{
level++;
}
// Other properties and methods all your components have in common
// Also get into "virtual" and "abstract" members!
}
and inherit your stuff from it like
public class Foundation : BaseComponent
{
// Additional stuff specific to the foundation
// overrides for the virtual and abstract members
}
public class Turret : BaseComponent
{
// Additional stuff specific to the turret
// overrides for the virtual and abstract members
}
//Maybe this would even inherit from Turret instead?
public class Turret2 : BaseComponent
{
// Additional stuff specific to the turret2
// overrides for the virtual and abstract members
}
and finally use that common base instead:
public void UpgradeComponent()
{
slot.GetComponent<BaseComponent>().Upgrade();
}
In my project, I have a class structure as shown in the image.
The green classes are old codes, that runs very well. The classes in red boxes are newly added codes. There're no compiler errors, however when click play in Unity and runs into the new code, the three classes can't be initialized correctly.
And unity console gives warning that says "The class named 'DataMgrBase`2' is generic. Generic MonoBehaviours are not supported! UnityEngine.GameObject:AddComponent()" at this line: "instance = obj.AddComponent ();"
How can I solve this problem?
Following are some code for your reference, thanks!
Implementation of singleton base class:
using UnityEngine;
using System.Collections;
public class UnitySingletonPersistent<T> : MonoBehaviour where T : Component
{
private static T instance;
public static T Instance {
get {
if (instance == null) {
instance = FindObjectOfType<T> ();
if (instance == null) {
GameObject obj = new GameObject ();
obj.name = typeof(T).Name;
obj.hideFlags = HideFlags.DontSave;
instance = obj.AddComponent<T> ();
}
}
return instance;
}
}
public virtual void Awake ()
{
DontDestroyOnLoad (this.gameObject);
if (instance == null) {
instance = this as T;
} else {
Destroy (gameObject);
}
}
}
Implementation of DataMgrBase:
public class DataMgrBase<TKey, TValue>: UnitySingletonPersistent<DataMgrBase<TKey, TValue>> {
protected Dictionary<TKey, TValue> dataDict;
public override void Awake()
{
base.Awake();
dataDict = new Dictionary<TKey, TValue>();
}
public TValue GetDataForKey(TKey key)
{
TValue data;
if (dataDict.TryGetValue(key, out data))
{
return data;
}
else
{
data = LoadDataForKey(key);
if (data != null)
{
dataDict.Add(key, data);
}
return data;
}
}
virtual protected TValue LoadDataForKey(TKey key)
{
if (dataDict.ContainsKey(key))
{
return GetDataForKey(key);
}
else
{
return default(TValue);
}
}
}
I've solved it by myself as following:
Change of the base class to get a new generic type(of the class that will derive from it, and pass this type to singleton base class)
public class DataMgrBase<TKey, TValue, TClass>: UnitySingletonPersistent<TClass> where TClass: Component
For all the other three classes that want to derive from it, change them as following form:
public class MobSettingDataMgr : DataMgrBase<int, MobSettingData, MobSettingDataMgr>
You want something like:
public abstract class UnitySingletonPersistent<T> : MonoBehaviour where T:UnitySingletonPersistent<T>
{
...
}
Then in your concrete class:
public class DataMgrBase<TKey, TValue> : UnitySingletonPersistent<DataMgrBase<TKey, TValue> >
{
...
}
This is somehow answer that is not solving your problem, but will explain the problem.
MonoBehaviour cannot be generic for at least two reason:
1. Imagine you want to add generic component in Inspector from Unity3D editor. Now engine needs to know exactly all types in this component, not only casue it is going to be compiled in this moment, but also cause you could have public fields with undeclered types. Try to assign your UnitySingletonPersistent directly in Inspector, and you will see it is imposible.
2. Using AddComponent<T> where T is generic looks like could work, but also in this engine you can make so called prefabs out of instantiated GameObjects, and if this GameObject contains generic component Unity3D engine would need to support some kind of baking types, and in practice this would lead to generating scripts, each with diffrent types, and making big mess inside project. I hope you follow me.
But why it works for the components you marked with green color? Simply cause Unity3D engine knows all types when adding this component to GameObject.
To support all this Unity Technologies would need to make core changes in Unity3D engine, how it works now. It would make Unity3D completly diffrent engine as it is now.
So to solve your problem, there is only one way: no adding in runtime generic components, and getting rid of DataMgrBase class. So you will need to implement DataMgrBase logic in each component.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# - Is there a better alternative than this to ‘switch on type’?
Consider the classic:
class Widget { }
class RedWidget : Widget { }
class BlueWidget : Widget { }
For the most part, in my UI, I can treat all Widgets the same. However, there are minor differences, which I need to if or switch through.
Possible approaches:
Enum Indicator - set by constructor
enum WidgetVariety { Red, Blue }
class Widget {
public WidgetVariety Variety { get; protected set; }
}
class RedWidget : Widget {
public RedWidget() {
Variety = Red;
}
}
// Likewise for BlueWidget...
switch (mywidget.Variety) {
case WidgetVariety.Red:
// Red specific GUI stuff
case WidgetVariety.Blue:
// Blue specific GUI stuff
}
Use is
Widget w = ...;
if (w is RedWidget) {
(RedWidget)w ...
}
else if (w is BlueWidget) {
(BlueWidget)w ...
}
The reason I've resorted to this is 1) Most of the code is already somewhat written this way, but much uglier. 2) 90% of the code is identical - basically just one column in a GridView needs to be handled differently depending on the type.
Which would you recommend? (Or anyone have a better solution?)
Edit I know I'll probably be recommended to the Visitor Pattern, but that simply seems to complicated for sparse, minor differences in this case.
Edit 2
So one particular difference I was having a hard time sorting out is this column that is different between the two types. In one case, it retrieves a bool value, and assigns that to the grid cell. In the other case, it gets a string value.
I suppose in this case, it should be obvious that I could define:
public object virtual GetColumn4Data();
public override GetColumn4Data() { return m_boolval; }
public override GetColumn4Data() { return m_mystring; }
This felt wrong to me initially, due to the use of object. However, that is the type of the property that I am assigning to in the cell, so of course this makes sense!
Too long at the office today it seems...
There's another possibility. Use virtual dispatch:
class Widget
{
public virtual void GuiStuff() { }
}
class RedWidget : Widget
{
public override void GuiStuff()
{
//... red-specific GUI stuff
base.GuiStuff();
}
}
class BlueWidget : Widget
{
public override void GuiStuff()
{
//... blue-specific GUI stuff
base.GuiStuff();
}
}
Subtype polymorphism is the best solution, avoiding this kind of checks is one of the main reasons OO was created.
Widget might have a method DoSomething() (abstract probably) and then RedWidget and BlueWidget would override it.
Also see Martin Fowler's Replace Conditional with Polymorphism:
Seen: You have a conditional that chooses different behavior depending on the type of an object.
Refactor: Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.
For your question under Edit #2, you could use a generic class to make the type vary among subclasses, though it may or may not work for you depending on your design. It will probably lead to other tough design decisions.
Rough example:
internal abstract class BaseClass
{
protected object mValue; // could also be defined as a T in BaseClass<T>
public object GetColumn4Data { get { return mValue; } }
}
// this is a group of classes with varying type
internal abstract class BaseClass<T> : BaseClass
{
public T GetTypedColumn4Data
{
get { return (T)mValue; }
set { mValue = value; }
}
}
// these are not really necessary if you don't plan to extend them further
// in that case, you would mark BaseClass<T> sealed instead of abstract
internal sealed class BoolSubClass : BaseClass<bool>
{
// no override necessary so far
}
internal sealed class StringSubClass : BaseClass<string>
{
// no override necessary so far
}
Notice, however, that you can't really get a single reference type that will have a varying return type on some property or method. A BaseClass reference will at best return a general type (like object).
If I have a class that is based off another class, how do I access the properties of the first class if it can have any name? I was thinking of using generics to access the properties, but the generics are "generic" for a reason...
For example:
public class AGameInXNA : Microsoft.Xna.Framework.Game
{
int ExampleGameProperty;
}
// ... another class ... //
public class ReferenceToAGameInXNA
{
Game gameInstance;
public void SetGameInstance(Game game)
{
gameInstance = game;
}
public void SetExampleGameProperty()
{
gameInstance.ExampleGameProperty = 21; // I don't know the name of
// AGameInXNA, so I want to
// access it using a generic
// class.
}
}
I know that that does not work, so how would I use generics in this case to access the AGameInXNA's properties in another class if I don't know AGameInXNA's name?
EDIT: I am trying to make it so that I can reuse this code later on. I want to be able to have a class that is unknown, such as public class unknownclassname that extends another class, such as Microsoft.Xna.Framework.Game, and be able to access the class unknownclassname without directly calling/implementing it in the library code.
I would recommend looking into XNA Services.
So for example, you would create a service which could be as simple as an
interface IExamplePropertyService
{
int ExampleProperty { get; set; }
}
public class AGameInXNA : Microsoft.Xna.Framework.Game, IExamplePropertyService
{
int ExampleGameProperty { get; set; }
void Initialize()
{
// Do other initialization
Services.Add( typeof(IExamplePropertyService), this );
}
}
public class ReferenceToAGameInXNA
{
IExamplePropertyService propertyService;
public void GetGameInstance(Game game)
{
propertyService = (IExamplePropertyService)game.GetService( typeof(IExamplePropertyService) );
}
public void SetExampleGameProperty()
{
propertyService.ExampleGameProperty = 21;
}
}
Implement it, and register it with the Game component, then in your ReferenceToAGameInXNA, you would query for this service and store it (rather than the Game) for use later.
As a bonus benefit, The IExamplePropertyService no longer even needs to be implemented by the Game class, it could be implemented by any GameComponent.
This makes for an easy way to seperate classes from having to know about the inner workings of other classes in the Game. So long as the services exist somewhere, your ReferenceToAGameInXNA can be used.
I don't think generics are what you are actually looking for here. In your second class, just change the type of all of the gameInstance to the type of the class you created for your game, in this case AGameInXNA. There should only be a need for one subclass of the Game type in each XNA game. That will allow you to access any public members of AGameInXNA from the Reference class.
If this isn't what you are after, please give a more detailed explanation of what you are trying to accomplish and I'll try to help you.
I don't know XNA, but if you want to have several classes that inherit from Game and have the same property on all of them, you could create an abstract class that inherits from Game and let the other classes inherit from that instead.
(Also, your GetGameInstance() is badly named, because it sets the field, it doesn't get it. And it's probably better as property anyway.)
public abstract class GameBase : Microsoft.Xna.Framework.Game
{
public int ExampleGameProperty { get; set; }
}
public class AGameInXNA : GameBase
{
// code specific to AGameInXNA
}
public class ReferenceToAGameInXNA
{
public GameBase GameInstance { get; set; }
public void SetExampleGameProperty()
{
GameInstance.ExampleGameProperty = 21;
}
}
If the other classed that have ExampleGameProperty shouldn't inherit from Game, you could create an interface instead. AGameInXNA would then inherit from Game directly and it would also implement the interface. And you would work with that interface in ReferenceToAGameInXNA.
using "Game gameInstance;" you can not acess ExmpleProp. You should use "AGameInXNA gameInstance;" too access ExampleProp.
I have 2 cases wheter a method can be considered a Factory Design Pattern, this example is in C#, altought, can apply to other programming languages:
enum NinjaTypes {
Generic,
Katanna,
StarThrower,
Invisible,
Flyer
}
public class Ninja {
public string Name { get; set; }
public void jump() { ... }
public void kickAss() { ... }
}
public class KatannaNinja: Ninja {
public void useKatanna() { ... }
}
public class StarNinja: Ninja {
public void throwStar() { ... }
}
public class InvisibleNinja: Ninja {
public void becomeInvisible() {...}
public void becomeVisible() {...}
}
public class FlyNinja: Ninja {
public void fly() {...}
public void land() {...}
}
public class NinjaSchool {
// always return generic type
public Ninja StandardStudent() {...}
// may return other types
public Ninja SpecialityStudent(NinjaTypes WhichType) {...}
}
The method StandardStudent() always return a new object of the same type, the SpecialityStudent(...), may return new objects from different classes that share the same superclass / base type. Both methods are intentionally not virtual.
The question is, are both methods "Factory Design Pattern" ?
My guess is that SpecialityStudent(...) is, but StandardStudent() is not. If the second is not, can be considered another design pattern ?
I don't think that nor a FactoryMethod`nor AbstractFactory patterns forbid the user to use a parameter to specify a type to the creator method. Anyway you should consider at least 2 things in your design:
Factory methods are useful to keep the client unaware of the concrete type of the created object. From my point of view isn't wrong to specify explicitly the type of object to be created, but pay attention to not put too much knowledge on the client classes to be able to construct objects through the factory.
Both your factory methods return a Ninja object, but some of your ninjas extended class declare additional methods, which client is unaware of. If your client need to use those methods explicitly then maybe you have to make some consideration on your design.
I think this actually looks like an Anti-Pattern. There's really nothing to stop a consumer of this code to just instantiate the specialty ninjas directly. What benefit is there to using the Ninja School? I think the whole point of the Factory pattern is to encapsulate the process of instantiating an object so that you can hide the details from the consumer. Any time you make a change to the "creation" logic, it doesn't break anyone's code.
And it just looks like a bad idea to have all the types in an enum. I don't have a concrete reason to back up this claim other than, "it feels wrong".
After reviewing the Abstract Factory pattern, I can see how you could go about turning this into an Abstract Factory, but I don't see the benefit given the semantics of your objects. I think that if you want to have a Ninja factory, you'd have to make the individual constructors protected or internal, so they can't be called directly by consumer code
Both your methods can be seen as factories. But the second one is a little awkward to use:
var school = new NinjaSchool();
var ninja = school.SpecialtyStudent(NinjaTypes.Flyer);
// to fly you must cast
((FlyingNinja)ninja).Fly();
You've already asked for a flyer, so you shouldn't need to cast. A better option might be to eliminate the enum and ask for the exact ninja that you want:
var flyingNinja = school.FlyingStudent(); // you get a FlyingNinja
flyingNinja.Fly();
Another thing to consider in your design is this: what if you want an invisible ninja that can fly? Or a katana ninja that also throws stars? That will shake up your hierarchy and challenge your belief in inheritance.
It's almost a factory method. I would do something like:
enum NinjaTypes {
Generic, Katanna, StarThrower, Invisible, Flyer
}
class Ninja {
String Name;
void jump() {
}
void kickAss() {
}
void useKatanna() {
System.out.println("nothing happens");
}
void throwStar() {
System.out.println("nothing happens");
}
void becomeInvisible() {
System.out.println("nothing happens");
}
void becomeVisible() {
System.out.println("nothing happens");
}
void fly() {
System.out.println("nothing happens");
}
void land() {
System.out.println("nothing happens");
}
}
class StarThrowerNinja extends Ninja {
void throwStar() {
System.out.println("throwing star");
}
}
class NinjaSchool {
static Ninja create(NinjaTypes WhichType) {
switch (WhichType) {
case Generic:
return new Ninja();
case StarThrower:
return new StarThrowerNinja();
default:
return null;
}
}
}
public class Main {
public static void main(String[] args) {
Ninja generic=NinjaSchool.create(NinjaTypes.Generic);
generic.throwStar();
Ninja starThrower=NinjaSchool.create(NinjaTypes.StarThrower);
starThrower.throwStar();
}
}