C#中的赋值运算符

时间:2016-12-29 18:42:44

标签: c# generics

我理解,与C ++不同,它不可能在C#中覆盖赋值运算符(=),如果我们想要将类C的实例i1分配给另一个实例i2(类),则必须创建一个复制方法C)。

但是这里陷入了两难境地: 我有一个通用的类T

public class Node<T>
{
  public Node<T> Next;
  public Node<T> Previous;
  T Content;

  public Node<T>()
  {
  }
}

T可以是类似Person的泛型类,也可以是C#标准类型,如int,double ...有时需要在节点中使用另一个内容T赋值(=)内容T的值。如果有一个可以在泛型类T中实现的赋值运算符,那么t1 = t2操作将适用于泛型类和标准类型,例如int,double ......等。这将是很好的。

您有什么建议,因为C#中不允许重载赋值运算符?我正在寻找一个适用于泛型类和标准类型的优雅解决方案,这样我的代码就不必区分不同的场景。

有可能吗?非常感谢!

4 个答案:

答案 0 :(得分:4)

您可以始终将给定类型的变量分配给该类型的另一个变量。

换句话说,写作:

public void Reassign(T source)
{
    Content = source;
}

永远有效。你必须处理许多类型的引用类型,所以分配的所有类型都是引用。如果要强制复制值,则需要添加一些泛型类型约束(ICloneable或类似的约束可行,但是您需要使用常见.NET类型的包装器。)

换句话说,如果你这样做了:

public class Node<T> where T : ICloneable

然后你可以写

public void Assign(T source)
{
    Content = source.Clone();
}

或者,如果您不需要以递归方式进行价值复制,则总是Object.MemberwiseClone

public void Assign(T source)
{
    Content = source.MemberwiseClone();
}

另外,如果你不知道,.NET已经在LinkedList<T>类中有一个双向链表。

答案 1 :(得分:1)

您不能重载赋值运算符,但可以创建显式或隐式类型转换运算符。这样就不必在你的情况下重载分配:

public static implicit operator Node<T>(T value) {
    Node<T> node = new Node<T>();
    node.Content = value;
    return node;
}

这使得这项任务完全成为可能:

int value = 1;
Node<int> node = value;

如果您将implicit替换为explicit,则需要进行明确的类型转换:

int value = 1;
Node<int> node = (Node<T>)value;

您也可以在相反的方向创建转化:

public static implicit operator T(Node<T> node) {
    return node.Content;
}

因此,您可以将节点分配给值:

Node<int> node = new Node<int>();
int value = node;

您还可以转换为不同的节点类型:

public static implicit operator Node<double>(Node<T> node) {
    Node<double> destinationNode = new Node<double>();
    destinationNode.Content = Convert.ToDouble(node.Content);
    return destinationNode;
}

这就是你所需要的。

答案 2 :(得分:1)

由于您创建的任何模板化类或对象必须采用数据类型,因此您应始终能够将一个分配给另一个。例如,在初始化Node<T>时,仍然需要采用数据类型:

Node<double> myNode = new Node<double>();

即使您有不同的实例,使用不同的数据类型,您也可以使用C#的Convert方法将它们从一个转换为另一个:https://msdn.microsoft.com/en-us/library/system.convert.aspx

Node<int> mySecondNode = new Node<int>();
Node<string> myThirdNode = new Node<string>();
Node<bool> myFourthNode = new Node<bool>();

myNode.Content = Convert.ToDouble(mySecondNode.Content);
myThirdNode.Content = myNode.ToString();
myFourthNode = Convert.ToBoolean(mySecondNode.Content % 2);

等等......重载运算符太棒了;但是C#让你很容易。此外,就像布拉德利所说的那样,C#库有一个LinkedList类,所以你不需要做所有繁重的工作。看看这里: https://msdn.microsoft.com/en-us/library/he2s3bh7(v=vs.110).aspx

答案 3 :(得分:1)

让我提出一个略微不同的结构,允许完全自定义内容类型。

public class Node<T> where T : Node<T>
{
    public T Next { get; set; }
    public T Previous { get; set; }
}

您不能直接使用此类,但您可以从中继承您的内容类型。例如:

public class Student : Node<Student>
{
    public int ID { get; set; }
    public string Name { get; set; }

    public override string ToString() { return string.Format("ID={0}, Name={1}", ID, Name); }
}

现在每个Student同时是内容和列表节点。 Node<T>类可重复使用。

现在您可以向节点类添加一些智能。例如:

public class Node<T> where T : Node<T>
{        
    public T Next { get; set; }
    public T Previous { get; set; }
    public bool IsRoot { get { return Previous==null; } }
    public bool IsLeaf { get { return Next==null; } }

    public int CountBefore { get { return IsRoot ? 0 : Previous.CountBefore+1; } }
    public int CountAfter { get { return IsLeaf ? 0 : Next.CountAfter+1; } }

    public T InsertAfter(T node)
    {
        var temp = this.Next;
        this.Next=node;
        if(node!=null)
        {
            node.Next=temp;
            node.Previous=this as T;
        }
        if(temp!=null)
        {
            temp.Previous=node;
        }
        return node;
    }
    public T InsertBefore(T node)
    {
        var temp = this.Previous;
        this.Previous=node;
        if(node!=null)
        {
            node.Previous=temp;
            node.Next=this as T;
        }
        if(temp!=null)
        {
            temp.Next=node;
        }
        return node;
    }
}

可以这样使用:

class Program
{
    static void Main(string[] args)
    {
        var A = new Student() { ID=101, Name="Peter" };
        var B = A.InsertAfter(new Student() { ID=102, Name="John" });
        var C = B.InsertBefore(new Student() { ID=103, Name="Mary" });

        //          [A]----[C]----[B]
        //           |      |      |
        // ID       101    103    102
        // Name    Peter  Mary   John
        // IsRoot  true   false  false 
        // IsLeft  false  false  true
        // CountL    0      1      2
        // CountR    2      1      0

    }
}