generic methods

I want to be able to implement a strongly typed IClonable, so that I can clone based on lineage.

public interface IClonableObject {
T Clone<T>(CloningFlags flags); // flags specify level of cloning
}


so that I have something that looks like:

public class Foo : IClonableObject, IFoo, IBar {
string bar = "";
public string Bar { get { return bar; } set { bar = value; } }
T Clone<T>(CloningFlags flags) {
switch(flags) {
....
case CloningFlags.ShallowCopy: {
return (T)MemberwiseClone();
}
}
}
}



and can use it like:

Foo f = new Foo();

Foo b = f.Clone<IFoo>(CloningFlags.ShallowCopy);



I know this syntax (and most likely logic) is wrong.


Answer this question

generic methods

  • kokob007

    that was what I implemented on my first iteration. the problem is that I have a fairly deep inheritance structure. so for each additional child I have to then implement that interface recursively throughout the child tree. needless to say ambiguity sets in and defeats the purpose of the generic interface. I think if I can have a generic method on a normal interface, that does pretty much the same thing it will work out.

  • h.Kavousi

    There is no way to get around having to implement the class specific methods in each derived class. However by using an abstract class (or even a regular class) you can implement most of the functionality in a base class. Continuing with the example of earlier.

    public interface ICloneableObject
    {
    T Clone (
    CloneFlags flags );
    }

    public abstract class CloneableObject : ICloneable
    {
    public T Clone<T> ( CloneFlags flags )
    {
    //Will blow up if the type isn't valid
    return (T)CloneObject(flags)
    }

    protected virtual object CloneObject ( CloneFlags flags )
    {
    switch(flags)
    {
    case CloneFlags.Shallow : return ShallowCopy(flags);
    case CloneFlags.Deep : return DeepCopy(flags);
    };

    return null;
    }

    protected virtual object ShallowCopy ( CloneFlags flags )
    {
    //Use built-in functionality
    return MemberwiseClone();
    }

    //No easy way to implement this by default
    protected abstract object DeepCopy ( CloneFlags flags );
    }

    public class Foo : CloneableObject
    {
    private string m_strName;
    public string Name
    {
    get { return m_strName ""; }
    set { m_strName = value; }
    }

    #region ICloneableObject Members
    protected override object DeepCopy ( CloneFlags flags )
    {

    //Do work
    }
    #endregion
    }

    The above situation allows derived classes to only mandatorially have to implement the deep copy. In all other cases they can override methods if they want. The only negative thing with the above implementation is that the base interface method is not virtual. You could modify the code to make it virtual.

    Michael Taylor - 8/2/06


  • Akshay S Bhatnagar

    You can create a generic interface for cloning but I'm not sure it'll be what you want.

    public enum CloneFlags
    {
    Shallow =
    0,
    Deep =
    1,
    }

    public interface ICloneableObject<T>
    {
    T Clone (
    CloneFlags flags );
    }

    public class Foo : ICloneableObject<Foo>
    {
    private string m_strName;
    public string Name
    {
    get { return m_strName ""; }
    set { m_strName = value; }
    }

    #region ICloneableObject<Foo> Members
    public Foo Clone ( CloneFlags flags )
    {
    switch (flags)
    {
    case CloneFlags.Shallow : return (Foo)MemberwiseClone();
    case CloneFlags.Deep: return this;
    };
    return null;
    }
    #endregion
    }

    This allows you to clone and return strongly typed objects. It is still up to each implementation to actually implement the cloning. You could create an abstract base class to hide the details and require that implementations override the deep and shallow cloning methods (perhaps providing default implementations) to avoid some work.

    Michael Taylor - 8/2/06


  • Thomas Pleasance

    I think the solution I gave earlier will work for you. You can have a generic method within a non-generic interface. In your case all you are really doing is casting the return value of the clone operation to a type that you want to use. Functionality it doesn't do anything else. Here is your sample code with cloning enabled.

    public enum CloneFlags { Shallow = 0, Deep, }
    public interface ICloneableObject
    {
    T Clone<T> (
    CloneFlags flags );
    }

    public interface I2dPoint { }
    public interface I3dPoint : I2dPoint { }

    public abstract class CloneableObject : ICloneableObject
    {
    public virtual T Clone<T>( CloneFlags flags )
    {
    object res = null;
    switch (flags)
    {
    case CloneFlags.Shallow: res = ShallowCopy(flags); break;
    case CloneFlags.Deep: res = DeepCopy(flags); break;
    };

    return (T)res;
    }

    protected
    virtual object ShallowCopy ( CloneFlags flags )
    {
    //Default implementation uses builtin functionality
    return this.MemberwiseClone();
    }

    protected virtual object DeepCopy ( CloneFlags flags )
    {
    //For grins print the type
    Console.WriteLine("{0}.DeepCopy", this.GetType().Name);

    //Default implementation does shallow copy first
    object target = this.MemberwiseClone();

    //Use reflection to enumerate all fields
    FieldInfo[] fields = target.GetType().GetFields();
    foreach(FieldInfo field in fields)
    {
    //If the field implements the ICloneableObject interface
    if (field.FieldType.GetInterface("ICloneableObject") != null)
    {
    //Get the field value
    ICloneableObject value = field.GetValue(this) as ICloneableObject;

    //Clone it
    if (value != null)
    {
    object newValue = value.Clone<object>(flags);

    //Set the new value
    field.SetValue(target, newValue);
    };
    }
    else if (field.FieldType.GetInterface("ICloneable") != null)
    {
    //Get the field value
    ICloneable value = field.GetValue(this) as ICloneable;

    //Clone it
    if (value != null)
    {
    object newValue = value.Clone();

    //Set the new value
    field.SetValue(target, newValue);
    };
    }
    else if (!field.FieldType.IsValueType)
    {
    throw new InvalidOperationException("Attempting to deep copy a reference type.");
    };
    };

    return target;
    }
    }

    public abstract class Base2dPoint : CloneableObject, I2dPoint { }
    public abstract class Base3dPoint : CloneableObject, I3dPoint { }
    public abstract class BaseGameObject : Base3dPoint { }
    public abstract class BaseInteractiveObject : BaseGameObject { }
    public class BaseNpc : BaseInteractiveObject { }
    public class BasePlayer : BaseInteractiveObject
    {
    public BaseItem LeftHand;
    public BaseItem RightHand;
    }
    public class BaseItem : BaseGameObject { }

    class Program
    {
    static void Main ( string[] args )
    {
    BasePlayer p1 = new BasePlayer();
    p1.LeftHand =
    new BaseItem();
    p1.RightHand =
    new BaseItem();

    BasePlayer p2 = p1.Clone<BasePlayer>(CloneFlags.Deep);

    BaseNpc npc1 = new BaseNpc();
    BaseNpc npc2 = npc1.Clone<BaseNpc>(CloneFlags.Deep);
    }
    }

    Notice a couple of things. Firstly is that the classes in the hierarchy don't implement the cloning methods but still support it. Secondly notice that because the cloning methods are virtual a class can implement them. Thirdly the base class uses reflection to clone the fields of each class.

    There are several problems with this solution and hence why MS doesn't provide direct support for it. Firstly is that using reflection is going to be really slow. So much so that it is not worth the effort. Therefore even though you have a base implementation you'll always want to define the DeepClone method in each class so that you can use field names and efficiently deep copy objects. This will have tremendous performance gains at the cost of maintenance. Given that the method is virtual you can call the base class implementation as well so each class only has to implement deep copying if they add new reference type fields.

    The other problem is that the generic cloning code only works with simple fields. If you want to support collections, lists, dictionaries or even arrays of objects then you'll have to extend the code to handle these cases. For example in each of these cases you'll actually need to create the collection, list or array of appropriate size and then clone each member. This will get complicated fast.

    Honestly I understand your goal but I don't think it'll be worth the effort it is going to require. However given the code above I think you can get a good start if you wish to pursue it further.

    Michael Taylor - 8/2/06


  • AzurianArcher

    I'm writing a server for a game. here's my object hierarchy:

    I 2d point -> 2d point
    | |
    v v
    I 3d point -> 3d point
    |
    v
    abstract game object
    |
    v
    abstact interactive object -> base npc
    | |
    v v
    base player gets ugly from here
    |
    v
    gets ugly from here


    so in another section I'm managing (creating, editing, removing) these game objects, and the code to do that is generic in that it can do whatever to whomever depending on criteria.

    so I'd like to have the abliity to specify IClonableObject.Clone<abstract interactive object>() then IClonableObject.Clone<2dpoint>() which would be quite a bit of mess if the interface were generic.

    does that make sense or am I misunderstanding what you're trying to say. (what you posted is how I first did this whole thing and how its still implemented, but its not working out right).

  • generic methods