简体   繁体   中英

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, ie

NonSubscriber mary = new NonSubscriber();

Then later on this "mary" subscribed to some services, and I want to change 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.

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

mary = new Subscriber();

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;
    }
}

创建一个将NonSubscriber对象作为参数的Subscriber构造函数,或者在NonSubscriber对象上创建一个返回Subscriber ,以免您不得不在多个位置编写映射代码。

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"; }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM