开发者

How to change the class of an object dynamically in C#?

Suppose I have a base class named Visitor, and it has 2 subclass Subscriber and NonSubscriber.

At first a visitor is start off from a NonSubscriber, i.e.

NonSubscriber mary = new NonSubscriber();

Then later on this "mary" subscribed to some services, and I want to chan开发者_Python百科ge the type of "mary" to Subscriber.

What is the conventional way to do that?


can't do that. sorry. C# is not a dynamic language.


You will have to create a new mary = new Subscriber(); and copy all relevant properties.

But a better approach might be to model it differently: Give Visitor a list of subscriptions. An empty list means a NonSubscriber.


You cant do this type of conversion. What you should do is treat mary as a visitor, and when time arrives, create a new instance of "subscriber":

Visitor mary = new NonSubscriber();
// Do some Visitor operations
...
// Now mary is a Subscriber
mary = new Subscriber();


You could use the GOF design patterns State or Strategy to model such an behaviour. Using these patterns, it seems during runtime as if the class of the objects has been changed.


It seems that you have some design problems. I think that it would be better to redesign your code like:

class Visitor
{
    private bool isSubscriber = false;

    public bool IsSubscriber
    {
         get { return isSubscriber; }
    }

    public void Subscribe()
    {
        // do some subscribing stuff
        isSubscriber = true;
    }

    public void Unsubscribe()
    {
        // do some unsubscribing stuff
        isSubscriber = false;
    }
}


You cannot change the type of a variable at runtime. You need to create a new instance.

mary = new Subscriber();


Create a Subscriber constructor that takes a NonSubscriber object as a parameter, or create a method on the NonSubscriber object that returns a Subscriber to save you having to writer the mappping code in multiple places.


It seems like you are encoding information incorrectly into your class hierarchy. It would make more sense to use a different pattern than sub classing here. For example, use only one class (visitor, or perhaps you could name it potential subscriber, whatever seems appropriate) and encode information on the services the object is subscribed to, moving the dynamically changing behavior behind a "Strategy" pattern or some such. There's very little detail in your example, but one thing you could do in C# is to make a "subscriber" property which would change the behavior of the object when the state of the property was changed.

Here's a contrived somewhat related example:

class Price
{
    private int priceInCents;
    private bool displayCents;

    private Func<string> displayFunction;

    public Price(int dollars, int cents)
    {
        priceInCents = dollars*100 + cents;
        DisplayCents = true;
    }

    public bool DisplayCents
    {
        get { return displayCents; }
        set
        {
            displayCents = value;
            if (displayCents)
            {
                this.displayFunction = () => String.Format("{0}.{1}", priceInCents / 100, priceInCents % 100);
            }
            else
            {
                this.displayFunction = () => (priceInCents / 100).ToString();
            }
        }
    }

    public string ToString()
    {
        return this.displayFunction();  
    }
}


public class User
{
    public Subscription Subscription { get; set; }
    public void HandleSubscription()
    {
        Subscription.Method();
    }
}

public abstract class SubscriptionType
{
  public abstract void Method();
}

public class NoSubscription : SubscriptionType
{
  public override void Method()
  {
    // Do stuff for non subscribers
  }
}

public class ServiceSubscription : SubscriptionType
{
  public override void Method()
  {
    // Do stuff for service subscribers
  }
}

public class Service2Subscription : SubscriptionType
{
  public override void Method()
  {
    // Do stuff for service2 subscribers
  }
}

Think the code explains my answer :)


Adding to the other answers and your comment, you indeed can use the state pattern for your purpose, it would go something like this:

public class MyProgram
{
    public void Run()
    {
        Visitor v = new Visitor("Mary");

        Debug.Assert(v.SubscriptionLinkText == "Join now");

        v.IsSubscribed = true;
        Debug.Assert(v.SubscriptionLinkText == "Today's special");

        v.IsSubscribed = false;
        Debug.Assert(v.SubscriptionLinkText == "Join now");
    }
}

public class Visitor
{
    public string Name { get; set; }

    private bool _isSubscribed;
    public bool IsSubscribed
    {
        get { return this._isSubscribed; }

        set
        {
            if (value != this._isSubscribed)
            {
                this._isSubscribed = value;
                this.OnSubscriptionChanged();
            }
        }
    }

    private SubscriptionBase _subscription;

    public string SubscriptionLinkText
    {
        get { return this._subscription.LinkText; }
    }

    public Visitor(string name)
    {
        this.Name = name;
        this._isSubscribed = false;
        this.OnSubscriptionChanged();
    }

    private void OnSubscriptionChanged()
    {
        // Consider also defining an event and raising it here

        this._subscription =
            SubscriptionBase.GetSubscription(this.IsSubscribed);
    }
}

abstract public class SubscriptionBase
{
    // Factory method to get instance
    static public SubscriptionBase GetSubscription(bool isSubscribed)
    {
        return isSubscribed ?
                new Subscription() as SubscriptionBase
                : new NoSubscription() as SubscriptionBase;
    }

    abstract public string LinkText { get; }
}

public class Subscription : SubscriptionBase
{
    public override string LinkText
    {
        get { return "Today's Special"; }
    }
}

public class NoSubscription : SubscriptionBase
{
    public override string LinkText
    {
        get { return "Join now"; }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜